Can I safely use CGFloat to hold Float64 or Float32 values? - iphone

I have an CGFloat property and sometimes I get a return value of type Float64 or also of type Float32. Could I store both safely to CGFloat?

From the headers:
// CGBase.h
typedef float CGFloat;
// MacTypes.h
typedef float Float32;
typedef double Float64;
So CGFloat and Float32 are both floats while Float64 is a double so you would lose precision.
(Edit to clarify: this is for 32 bit systems such as the iPhone. If you are building for 64 bit, CGFloat is defined as a double.)

It's best practice to always try and store scalar values in the same type as you received them because the precision of scalar types changes with the hardware.
CGFloat isn't always guaranteed to be the same size on all current and future hardware. If you substitute another type for it or use it to store another type, your code made break somewhere down the road.
You might gain or lose precision when a new iPhone/iPad comes out or the code might break if you try to port it to Macs.

Related

How to deal with the lack of `simd_packed_float3` in Swift

There is no simd_packed_float3 type in Swift.
Why it's a problem?
Consider this Metal struct:
struct Test{
packed_float3 x;
float y;
};
First of all, you can't calculate a buffer pointer to address the memory of y, since you can't do this:
MemoryLayout<simd_packed_float3>.size
(Not sure if stride makes sense with packed types, but anyway with simd types it always gives the same length as size on my devices)
You can't use MemoryLayout<simd_float3>.size either, since it will return 16 and not 12 like in architectures available to me for testing.
Second, if you need to write a packed_float3 value of x to the buffer you will need to write the three consecutive floats, but not a single simd type. Again, simd_float3 is not usable since it will write 0 into the forth word corrupting the memory of the next property in the struct (y).
So I've done this:
struct Float_3{
var x: Float
var y: Float
var z: Float
}
typealias simd_packed_float3 = Float_3
It seems to be a functioning solution, but I'm not sure it's not a nasty thing to do...
What problems may I encounter with this approach, and how could I be sure that it won't break on some device that I don't have?
You can define a packed struct in your bridging header:
struct __attribute__((packed)) PackedFloat3 {
float x;
float y;
float z;
};
MemoryLayout<PackedFloat3>.size == 12
MemoryLayout<PackedFloat3>.stride == 12
By the way, simd_float3 is 16 bytes everywhere, simd types have stricter alignment requirements.
You can also typedef it to packed_float3 under #ifndef #ifdef __METAL_VERSION__ to have the same spelling in Swift and MSL.
The reason to do it in bridging header instead of Swift is that you can use the same structs with same spelling in both shaders and Swift.
I'm answering this following the answers I received on the Swift forum.
Turns out that someone in the Metal team at Apple has already thought of this problem and created the MTLPacked types exactly for the types that would have irregular sizes:
MTLPackedFloat3
MTLPackedFloat4x3

How to determine if CGFloat is Float or Double [duplicate]

This question already has answers here:
Should conditional compilation be used to cope with difference in CGFloat on different architectures?
(3 answers)
Closed 6 years ago.
Quartz uses CGFloat for its graphics. CGFloat is either Float or Double, depending on the processor.
The Accelerate framework has different variations of the same function.
For example dgetrf_ for Double's and sgetrf_ for Float's.
I have to make these two work together. Either I can use Double's everywhere and convert them to CGFloat every time I use quartz, or I can (try to) determine the actual type of CGFloat and use the appropriate Accelerate function.
Mixing CGFloat's and Double types all over my code base is not very appealing and converting thousands or millions of values to CGFloat every time doesn't strike me as very efficient either.
At this moment I would go with the second option. (Or shouldn't I?)
My question is: how do I know the actual type of CGFloat?
if ??? //pseudo-code: CGFloat is Double
{
dgetrf_(...)
}
else
{
sgetrf_(...)
}
Documentation on Swift Floating-Point Numbers:
Floating-point types can represent a much wider range of values than
integer types, and can store numbers that are much larger or smaller
than can be stored in an Int. Swift provides two signed floating-point
number types:
Double represents a 64-bit floating-point number.
Float represents a 32-bit floating-point number.
You can test using the sizeof function:
if sizeof(CGFloat) == sizeof(Double) {
// CGFloat is a Double
} else {
// CGFloat is a Float
}
Probably the easiest way to deal with this is to use conditional compilation to define a wrapper which will call the proper version:
import Accelerate
func getrf_(__m: UnsafeMutablePointer<__CLPK_integer>,
__n: UnsafeMutablePointer<__CLPK_integer>,
__a: UnsafeMutablePointer<CGFloat>,
__lda: UnsafeMutablePointer<__CLPK_integer>,
__ipiv: UnsafeMutablePointer<__CLPK_integer>,
__info: UnsafeMutablePointer<__CLPK_integer>) -> Int32 {
#if __LP64__ // CGFloat is Double on 64 bit archetecture
return dgetrf_(__m, __n, UnsafeMutablePointer<__CLPK_doublereal>(__a), __lda, __ipiv, __info)
#else
return sgetrf_(__m, __n, UnsafeMutablePointer<__CLPK_real>(__a), __lda, __ipiv, __info)
#endif
}
There is a CGFLOAT_IS_DOUBLE macro defined in Core Graphics. You can use it in Swift for direct comparison:
if CGFLOAT_IS_DOUBLE == 1 {
print("Double")
} else {
print("Float")
}
Of course, direct size comparison is also possible:
if sizeof(CGFloat) == sizeof(Double) {
}
However, since there are overloaded functions for all Float, Double and CGFloat, there is rarely a reason to inspect the size of the type.

What's the largest variable value in iphone?

I need to assign 2,554,416,000 to a variable. What would be the primitive to use, and what would be the object representation class to use? Thanks.
Chuck is right, but in answer to the "object representation", you want NSNumber used with the unsignedInt methods.
NSNumber *myNum = [NSNumber numberWithUnsignedInt:2554416000];
NSUInteger myInt = [myNum unsignedIntValue];
2,554,416,000 = 0x9841,4B80 ≤ 0xFFFF,FFFF (UINT_MAX), so uint32_t (unsigned int) or int64_t (long long).
A signed int32_t (int) cannot represent this because 0x9841,4B80 > 0x7FFF,FFFF (INT_MAX). Storing it in an int will make it negative.
This can be represented by a 32-bit unsigned integer (UINT_MAX is about 4 billion). That's actually what NSUInteger is on the iPhone, but if you want to be very specific about the bit width, you could specify a uint32_t.
You could store it in a regular int scaled down by 1000 if you wanted, if this represented a score that could never have the bottom 3 digits hold any info or something similiar. This would be a way to save a few bits and possibly an entire extra int of space, if that matters.

How to safely create an NSInteger from an CGFloat?

I've got an CGFloat but need it as an NSInteger. The float value is like 2.0f, so I don't mind about fractional parts and loosing precision. What's a legal way to convert it into NSInteger without trouble (except the loss of precision, of course)?
NSInteger niceInt = niceCGFloat;
seems too simple, smells buggy. Maybe you can explain?
You want the c function lrintf() which rounds a floating point to a long int.
There's always the risk that 2.0f may actually be 1.9999999999f when represented in binary. Your conversion to int would then lead to 1 instead of 2.
To avoid this, I would add 0.5f to your float value. This would also have the effect of rounding your float, instead of truncating it.
NSInteger niceInt = niceCGFloat + 0.5f;

Secure and valid way for converting a "float integer" into a natural integer?

I have a CGFloat that contains only "integers", in the meaning that it actually wants to represent integers only but due to float inprecision it may happen that it internally has a 23.00000000000000000000000142 or something like that. I try to feed an NSDecimalNumber with clean input, so I need to make sure this is truly a naked integer with no dirt. Well, I think this is a good way, but maybe I am wrong:
NSInteger *intVal = floatVal;
That would just get rid of those fragmental parts at the end of the tale, right? Or is there a more secure way to convert it into a true integer?
If you just want to take the integer part of a float then I believe you can just do the following:
CGFloat myfloat = 23.0000000000142f;
int myInt = (int) myfloat;