Bug in fminf and fmaxf on iOS 64bit processors - ios

I came across a bug with the 64bit processors that I wanted to share.
CGFloat test1 = 0.58;
CGFloat test2 = 0.40;
CGFloat value;
value = fmaxf( test1, test2 );
The result would be:
value = 0.5799999833106995
This obviously is a rounding issue, but if you needed to check to see which value was picked you would get an erroneous result.
if( test1 == value ){
// do something
}
however if you use either MIN( A, B ) or MAX( A, B ) it would work as expected.
I thought this is was worth sharing
Thanks

This has nothing to do with a bug in fminf or fmaxf. There is a bug in your code.
On 64-bit systems, CGFloat is typedef'd to double, but you're using the fmaxf function, which operates on float (not double), which causes its arguments to be rounded to single precision, thus changing the value. Don't do that.
On 32-bit systems, this doesn't happen because CGFloat is typedef'd to float, matching the argument and return type of fmaxf; no rounding occurs.
Instead, either include <tgmath.h> and use fmax without the f suffix, or use float instead of CGFloat.

Related

Why does the compiler claim CGRect has no width member?

Note that I'm not trying to set the value in a CGRect. I'm mystified as to why the compiler is issuing this claim:
let widthFactor = 0.8
let oldWidth = wholeFrameView.frame.width
let newWidth = wholeFrameView.frame.width * widthFactor // Value of type '(CGRect) -> CGRect' has no member 'width'
let newWidth2 = wholeFrameView.frame.width * 0.8 // This is fine.
Width is a CGFloat where your multiplier is a Double. Explicitly declare the type of your multiplier:
let widthFactor: CGFloat = 0.8
All the dimensions of a CGRect are of type CGFloat, not Double, and because Swift is especially strict about types, you can't multiply a CGFloat by a Double.
The interesting thing though, is that both CGFloat and Double implement ExpressibleByFloatLiteral. This means that 0.8, a "float literal", can be interpreted as either a Double or a CGFloat. Without context, it's always a Double, because of how the compiler is designed. Note that this only applies to float literals like 3.14, 3e8 etc, and not to identifiers of variables.
So the expression wholeFrameView.frame.width * 0.8 is valid because the compiler sees that width is a CGFloat, so it treats 0.8 as a CGFloat as well. No problems.
On the other hand, when you declare the variable widthFactor, it is automatically given the type Double, because there aren't any more context on that line to suggest to the compiler that you want it to be any other type.
This can be fixed by directly telling the compiler that you want widthFactor to be a CGFloat:
let widthFactor: CGFloat = 0.8
Because, as others have noted, you can't multiply a Double and a CGFloat, the compiler doesn't know what you're intending.
So, instead of giving you an error about the frame property, which you currently think it's doing, it's actually making its best guess*, and giving you an error related to the frame method. No method method has a width property, so what it tells you is true.
*Of course, its best guess is not good enough, hence a human being coming here to ask a question about it. So please file a bug!
Stepping onto my soapbox: This confusion would be avoided if Apple hadn't named the method the thing it returns. The convention to prefix all such methods with get solves the problem. Some convention is important in any language with first-class functions, to disambiguate between properties and methods.
wholeFrameView.frame has no member width. Also, you need widthFactor to be of type CGFloat. Try:
let newWidth = wholeFrameView.frame.size.width * CGFloat(widthFactor)

Int to format %.2f returns unexpected number on iPhone 5

I'm passing 0 as an argument to String(format: "%.2f"), it works on iPhone 5s, se, 6, 6s etc as expected ... However, it stopped working on iPhone 5, I guessed that it was a problem of 32 bit and 64 bit systems, because %f formats 64-bit floating-point number. Wrapped 0 with Double(0) and it worked, result was 0.00.
Can someone explain it in more details ?
String(format:) uses the same conversion specifications as
printf
(with some additions like %# for objects). In particular, the %f
conversion expects a Double on the argument list, and passing
anything else causes undefined behaviour: It may produce unexpected
output or crash.
On a 64-bit platform, passing 0 may work by chance because then
Int is a 64-bit integer and thus has the same size as a Double.
But even that is not guaranteed to work:
passing an integer argument instead of the expected floating
pointer number is still undefined behaviour.
You can use swift inbuilt method for a more consistent behavior
// Round the given value to a specified number
// of decimal places
func round(_ value: Double, toDecimalPlaces places: Int) -> Double {
let divisor = pow(10.0, Double(places))
return round(value * divisor) / divisor
}
Example:
round(52.3761, toDecimalPlaces: 3) // 52.376
round(52.3761, toDecimalPlaces: 2) // 52.38

Computing remainder without using modulus operator F#

I have implemented my code which is basically to compute remainder in two numbers without using modulus operator however, I am stuck in a situation which is just hectic. I know the logic however I am newbie in f# and dont know how to implement it.
let rec modulus a b =
if b = 0 Console.WriteLine("Sorry Wrong Divisor")
let bool neg = a < 0
a = abs a
b = abs b
modulus(val-divisor,divisor)
All I know is I am getting a pretty basic mistake here, Any help,
The first step towards getting this to work is to fix your indentation and turn your sketch into valid F# code that actually compiles and runs - that should help you get to the next step, which is to fix the logic of the implementation.
A minimal code that is similar to yours and actually runs looks like this:
let rec modulus value divisor : int =
printfn "value=%d, divisor=%d" value divisor
if divisor = 0 then Console.WriteLine("Sorry Wrong Divisor")
let neg = value < 0
let value = abs value
let divisor = abs divisor
modulus (value-divisor) divisor
modulus 10 5
I fixed the indentation - F# is indentation sensitive, so this matters.
I replaced your a = abs a with let - the let keyword defines a new variable, hiding the existing one (as you cannot mutate existing variables - they are immutable in F#)
I renamed your variables to consistently use divisor and value names
I added printfn so that you can see how the function runs (it will get into an infinite loop, because it currently never checks for the termination condition!)
I had to add type annotation : int to say that the result will be int - as your function never returns, this is required (but you can remove it once you fix this)
You can calculate modulus in simpler way
let modulus a b=
if b = 0.0 then failwith "Cannot divide by zero"
a - b * truncate(a / b);

What am I doing wrong with Swift and basic geometry

When I use the hard coded data for diameter and height Swift runs simulator fine, but when I try and use text values in place using TextField.text.toInt() then I keep getting very annoying error message of:-
cannot invoke \ with an argument list of type $st15
Just where am I going wrong. I am new to Swift and have only used AppInventor before to create an app with a few thousand downloads. I am a enthusiastic but probably slow learner, but I will get there if someone would be so kind to help me out a little. Note: Formula is simply PIr2 x h to give cylinder volume. I want to use diameter which explains why I am halving each time.
let PI = 3.142
var bodyDiameter = bodyDiameterTextField.text.toInt() // 3.0
var bodyHeight = bodyHeightTextField.text.toInt() // 10.0
var cylinderVolume: Double
var cylinderVolume = (PI * (bodyDiameter / 2.0) * (bodyDiameter / 2.0)) * bodyHeight
println("cylinderVolume")
cylinderVolumeLabel.text = "(cylinderVolume)"
Here follows same code with hard coded values for Bodydiameter and Bodyheight. It all works great in the playground and the simulator. I guess its got something to do with Integers and Floats, but I'm probably way out.
let PI = 3.142
var bodyDiameter = 3.0
var bodyHeight = 10.0
var cylinderVolume = (PI * (bodyDiameter / 2.0) * (bodyDiameter / 2.0)) * bodyHeight
println("cylinderVolume")
cylinderVolumeLabel.text = "(cylinderVolume)"
Your code has several errors.
First, and not really an error, there's already a built-in constant for π, named M_PI. Use that instead of defining your own PI constant.
Next, String.toInt() returns an Int?. The question mark means the return type is really Optional<Int>. This is a container that is either empty (nil), or contains an Int. If you want to use the Int value, you need to unwrap it. You might want to check that the Optional isn't nil first, though.
Next, assuming you unwrap the Int, you can't perform arithmetic on mixed Int and Double values in Swift. You have to convert to all Int or all Double. You probably want to use all Double. In fact, you probably don't want to convert from Int to Double at all. You probably want to get a Double from the text field in the first place. There's no toDouble on String in Swift, but there are some other ways to do it.
Finally, you need to say \(cylinderVolume) to interpolate the value into the string. Your code omits the \.
Try this:
var bodyDiameter = (bodyDiameterTextField.text as NSString).doubleValue
var bodyHeight = (bodyHeightTextField.text as NSString).doubleValue
var cylinderVolume = (M_PI * (bodyDiameter / 2.0) * (bodyDiameter / 2.0)) * bodyHeight
println("cylinderVolume: \(cylinderVolume)")
cylinderVolumeLabel.text = "(cylinderVolume)"
If you want to convert the strings to Doubles in a localization-friendly way, or detect when the strings aren't valid Doubles, look up NSNumberFormatter.

Encoding CGPoint with NSCoder, with full precision

In my iOS app, I have a shape class, built with CGPoints. I save it to a file using encodeCGPoint:forKey. I read it back in. That all works.
However, the CGPoint values I read in are not exactly equal to the values I saved it. The low bits of the CGFloat values aren't stable. So CGPointEqualToPoint returns NO, which means my isEqual method returns NO. This causes me trouble and pain.
Obviously, serializing floats precisely has been a hassle since the beginning of time. But in this situation, what is the best approach? I can think of several:
write out the x and y values using encodeFloat instead of encodeCGPoint (would that help at all?)
multiply my x and y values by 256.0 before saving them (they're all going to be between -1 and 1, roughly, so this might help?)
write out the x and y values using encodeDouble instead of encodeCGPoint (still might round the lowest bit incorrectly?)
cast to NSUInteger and write them out using encodeInt32 (icky, but it would work, right?)
accept the loss of precision, and implement my isEqual method to use within-epsilon comparison rather than CGPointEqualToPoint (sigh)
EDIT-ADD: So the second half of the problem, which I was leaving out for simplicity, is that I have to implement the hash method for these shape objects.
Hashing floats is also a horrible pain (see " Good way to hash a float vector? "), and it turns out it more or less nullifies my question. The toolkit's encodeCGPoint method rounds its float values in an annoying way -- it's literally printing them to a string with the %g format -- so there's no way I can use it and still make hashing reliable.
Therefore, I'm forced to write my own encodePoint function. As long as I'm doing that, I might as well write one that encodes the value exactly. (Copy two 32-bit floats into a 64-bit integer field, and no, it's not portable, but this is iOS-only and I'm making that tradeoff.)
With reliable exact storage of CGPoints, I can go back to exact comparison and any old hash function I want. Tolerance ranges do nothing for me, so I'm just not using them for this application.
If I wanted hashing and tolerance comparisons, I'd be comparing values within a tolerance of N significant figures, not a fixed distance epsilon. (That is, I'd want 0.123456 to compare close to 0.123457, but I'd also want 1234.56 to compare close to 1234.57.) That would be stable against floating-point math errors, for both large and small values. I don't have sample code for that, but start with the frexpf() function and it shouldn't be too hard.
Directly comparing floating point numbers is usually not the right game plan. Try one of the many other options. The best solution for your problem is probably your last suggestion; I don't know why there's a "sigh" there, though. A double precision floating point number has about 16 decimal digits worth of precision - there's a very good chance that your program doesn't actually need that much precision.
Use the epsilon method, because the "low bits of the CGFloat values aren't stable" problem surfaces any time there's an implicit conversion between float and double (often in framework code. tgmath.h is useful for avoiding this in your own code.)
I use the following functions (the tolerance defaulting to 0.5 because that's useful in the common case for CGGeometry):
BOOL OTValueNearToValueWithTolerance(CGFloat v1, CGFloat v2, CGFloat tolerance)
{
return (fabs(v1 - v2) <= tolerance);
}
BOOL OTPointNearToPointWithTolerance(CGPoint p1, CGPoint p2, CGFloat tolerance)
{
return (OTValueNearToValueWithTolerance(p1.x, p2.x, tolerance) && OTValueNearToValueWithTolerance(p1.y, p2.y, tolerance));
}
BOOL OTSizeNearToSizeWithTolerance(CGSize s1, CGSize s2, CGFloat tolerance)
{
return (OTValueNearToValueWithTolerance(s1.width, s2.width, tolerance) && OTValueNearToValueWithTolerance(s1.height, s2.height, tolerance));
}
BOOL OTRectNearToRectWithTolerance(CGRect r1, CGRect r2, CGFloat tolerance)
{
return (OTPointNearToPointWithTolerance(r1.origin, r2.origin, tolerance) && OTSizeNearToSizeWithTolerance(r1.size, r2.size, tolerance));
}
BOOL OTValueNearToValue(CGFloat v1, CGFloat v2)
{
return OTValueNearToValueWithTolerance(v1, v2, 0.5);
}
BOOL OTPointNearToPoint(CGPoint p1, CGPoint p2)
{
return OTPointNearToPointWithTolerance(p1, p2, 0.5);
}
BOOL OTSizeNearToSize(CGSize s1, CGSize s2)
{
return OTSizeNearToSizeWithTolerance(s1, s2, 0.5);
}
BOOL OTRectNearToRect(CGRect r1, CGRect r2)
{
return OTRectNearToRectWithTolerance(r1, r2, 0.5);
}
BOOL OTPointNearToEdgeOfRect(CGPoint point, CGRect rect, CGFloat amount, CGRectEdge edge)
{
CGRect nearRect, otherRect;
CGRectDivide(rect, &nearRect, &otherRect, amount, edge);
return CGRectContainsPoint(nearRect, point);
}

Resources