swift 2.0 NSDecimalNumber possible discrepency converting to long - ios

I can't make heads or tails of this. I am using NSDecimalNumber to truncate
the fractional portion from a string. This works in most cases, but not apparently in the case of infinite decimals (or just too many). Here is an example:
print(NSDecimalNumber(string: "49.81666666666666666").longLongValue)
print(NSDecimalNumber(string: "49.816666666666666666").longLongValue)
print(NSDecimalNumber(string: "49.8166666666666666666").longLongValue)
The first line prints 49, the second -5, and the last one 0. I know I can use the rounding function to do the same thing, and that is what I will probably use instead, but doesn't this seem odd? I know it isn't just converting the float bit pattern into a long or else the results would be completely different.

Related

Delphi Roundto and FormatFloat Inconsistency

I'm getting a rounding oddity in Delphi 2010, where some numbers are rounding down in roundto, but up in formatfloat.
I'm entirely aware of binary representation of decimal numbers sometimes giving misleading results, but in that case I would expect formatfloat and roundto to give the same result.
I've also seen advice that this is the sort of thing "Currency" should be used for, but as you can see below, Currency and Double give the same results.
program testrounding;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,Math;
var d:Double;
c:Currency;
begin
d:=534.50;
c:=534.50;
writeln('Format: ' +formatfloat('0',d));
writeln('Roundto: '+formatfloat('0',roundto(d,0)));
writeln('C Format: ' +formatfloat('0',c));
writeln('C Roundto: '+formatfloat('0',roundto(c,0)));
readln;
end.
The results are as follows:
Format: 535
Roundto: 534
C Format: 535
C Roundto: 534
I've looked at Why is the result of RoundTo(87.285, -2) => 87.28 and the suggested remedies do not seem to apply.
First of all, we can remove Currency from the question, because the two functions that you use don't have Currency overloads. The value is converted to an IEEE754 floating point value and then follows the same path as your Double code.
Let's look at RoundTo first of all. It is quick to check, using the debugger, or an additional Writeln that RoundTo(d,0) = 534. Why is that?
Well, the documentation for RoundTo says:
Rounds a floating-point value to a specified digit or power of ten using "Banker's rounding".
Indeed in the implementation of RoundTo we see that the rounding mode is temporarily switched to TRoundingMode.rmNearest before being restored to its original value. The rounding mode only applies when the value is exactly half way between two integers. Which is precisely the case we have here.
So Banker's rounding applies. Which means that when the value is exactly half way between two integers, the rounding algorithm chooses the adjacent even integer.
So it makes sense that RoundTo(534.5,0) = 534, and equally you can check that RoundTo(535.5,0) = 536.
Understanding FormatFloat is quite a different matter. Quite frankly its behaviour is somewhat opaque. It performs an ad hoc rounding in code that differs for different platforms. For instance it is assembler on 32 bit Windows, but Pascal on 64 bit Windows. The overall approach appears to be to take the mantissa of the floating point value, convert it to an integer, convert that to text digits, and then perform the rounding based on those text digits. No respect is paid to the current rounding mode when the rounding is performed, and the algorithm appears to implement the round half away from zero policy. However, even that is not implemented robustly for all possible floating point values. It works correctly for your value, but for values with more digits in the mantissa the algorithm breaks down.
In fact it is fairly well known that the Delphi RTL routines for converting between floating point values and text are fundamentally broken by design. There are no routines in the Delphi RTL that can correctly convert from text to float, or from float to text. In fact, I have recently implemented my own conversion routines, that do this correctly, based on existing open source code used by other language runtimes. One of these days I will get around to publishing this code for use by others.
I'm not sure what your exact needs are, but if you are wishing to exert some control over rounding, then you can do so if you take charge of the rounding. Whilst RoundTo always uses Banker's rounding, you can instead use Round which uses the current rounding mode. This will allow you to perform the round using the rounding algorithm of your choice (by calling SetRoundMode), and then you can convert the rounded value to text. That's the key. Keep the value in an arithmetic type, perform the rounding, and only convert to text at the very last moment, after the correct rounding has been applied.
In this case, the value 534.5 is exactly representable in Double precision.
Looking into source code, reveals that the FormatFloat function rounds upwards if the last pending digit is 5 or more.
RoundTo uses the Banker's rounding, and rounds to nearest even number (534) in this case.

Convert Large NSString in Hexadecimal to Decimal NSString iOS

As the title of the question states I'm looking to take the following string in hexadecimal base:
b9c84ee012f4faa7a1e2115d5ca15893db816a2c4df45bb8ceda76aa90c1e096456663f2cc5e6748662470648dd663ebc80e151d4d940c98a0aa5401aca64663c13264b8123bcee4db98f53e8c5d0391a7078ae72e7520da1926aa31d18b2c68c8e88a65a5c221219ace37ae25feb54b7bd4a096b53b66edba053f4e42e64b63
And convert it to its decimal equivalent string:
130460875511427281888098224554274438589599458108116621315331564625526207150503189508290869993616666570545720782519885681004493227707439823316135825978491918446215631462717116534949960283082518139523879868865346440610923729433468564872249430429294675444577680464924109881111890440473667357213574597524163283811
I've looked to use this code, found at this link:
unsigned result = 0;
NSScanner *scanner = [NSScanner scannerWithString:hexString];
[scanner setScanLocation:1]; // bypass '#' character
[scanner scanHexInt:&result];
NSLog(#" %u",result);
However, I keep getting the following result: 4294967295. Any ideas on how I can solve this problem?
This sounds like a homework/quiz question, and SO isn't to get code written, so here are some hints in hope they help.
Your number is BIG, far larger than any standard integer size, so you are not going to be able to do this with long long or even NSDecimal.
Now you could go and source an "infinite" precision arithmetic package, but really what you need to do isn't that hard (but if you are going to be doing more than this then such using a package would make sense).
Now think back to your school days, how were you taught to do base conversion? The standard method is long division and reminders.
Example: start with BAD in hex and convert to decimal:
BAD ÷ A = 12A remainder 9
12A ÷ A = 1D remainder 8
1D ÷ A = 2 remainder 9
2 ÷ A = 0 remainder 2
now read the remainder back, last first, to give 2989 decimal.
Long division is a digit at a time process, starting with the most significant digit, and carrying the remainder as you move to the next digit. Sounds like a loop.
Your initial number is a string, the most significant digit is first. Sounds like a loop.
Processing characters one at a time from an NSString is, well, painful. So first convert your NSString to a standard C string. If you copy this into a C-array you can then overwrite it each time you "divide". You'll probably find the standard C functions strlen() and strcpy() helpful.
Of course you have characters in your string, not integer values. Include ctype.h in your code and use the digittoint() function to convert each character in your number to its numeric equivalent.
The standard library doesn't have the inverse of digittoint(), so to convert an integer back to its character equivalent you need to write your own code, think indexing into a suitable constant string...
Write a C function, something like int divide(char *hexstring) which does one long division of hexstring, writing the result into hexstring and returning the remainder. (If you wish to write more general code, useful for testing, write something like int divide(char *buf, int base, int divisor) - so you can convert hex to decimal and then back again to check you get the back to where you started.)
Now you can loop calling your divide and accumulating the remainders (as characters) into another string.
How big should your result string be? Well a number written in decimal typically has more digits than when written in hex (e.g. 2989 v. BAD above). If you're being general then hex uses the fewest digits and binary uses the most. A single hex digit equates to 4 binary digits, so a working buffer 4 times the input size will always be long enough. Don't forget to allow for the terminating NUL in C strings in your buffer.
And as hinted above, for testing make your code general, convert your hex string to a decimal one, then convert that back to a hex one and check the result is the same as the input.
If this sounds complicated don't despair, it only takes around 30 lines of well spaced code.
If you get stuck coding it ask a new question showing your code, explain what goes wrong, and somebody will undoubtedly help you out.
HTH
Your result is the maximum of unsinged int 32 bit, the type you are using. As far as I can see, in the NSScanner documentation long long is the biggest supported type.

When is it better to use an NSDecimal, NSDecimalNumber instead of a double?

For simple uses, such as tracking weight values like 65.1kg, is there any benefit of going with NSDecimal/NSDecimalNumber over double?
My understanding here is double (or even float) provides more than enough precision in such cases. Please correct me if I'm wrong.
First, read Josh Caswell's link. It it especially critical when working with money. In your case it may matter or may not, depending on your goal. If you put in 65.1 and you want to get exactly 65.1 back out, then you definitely need to use a format that rounds properly to decimal values like NSDecimalNumber. If, when you put in 65.1, you want "a value that is within a small error of 65.1," then float or double are fine (depending on how much error you are willing to accept).
65.1 is a great example, because it demonstrates the problem. Here in Swift because its so easy to demonstrate, but ObjC is the same:
1> 65.1
$R0: Double = 65.099999999999994
2>
1/10 happens to be a repeating decimal in binary, just like 1/3 is a repeating decimal in decimal. So 65.1 encoded as a double is "close to" 65.1, but not exact. If you need an exact representation of decimal-encoded number (i.e. what most humans expect), use NSDecimalNumber. This isn't to say that NSDecimalNumber is more accurate than double. It just imposes different rounding errors than double. Which rounding errors you prefer depends on your use case.

Comparing NSNumber instances with isEqualToNumber

Yes, I've read the other posts on stackoverflow about comparing NSNumber and none of them seem to quite address this particular situation.
This solution was particularly bad ... NSNumber compare: returning different results
Because the suggested solution doesn't work at all.
Using abs(value1 - value2) < tolerance is flawed from the start because fractional values are stripped off, making the tolerance irrelevant.
And from Apple documentation... NSNumber explicitly doesn't guarantee that the returned type will match the method used to create it. In other words, if you're given an NSNumber, you have no way of determining whether it contains a float, double, int, bool, or whatever.
Also, as best I can tell, NSNumber isEqualToNumber is an untrustworthy method to compare two NSNumbers.
So given these definitions...
NSNumber *float1 = [NSNumber numberWithFloat:1.00001];
NSNumber *double1 = [NSNumber numberWithDouble:1.00001];
If you run the debugger and then do 2 comparisons of these identical numbers using ==, one fails, and the other does not.
p [double1 floatValue] == [float1 floatValue] **// returns true**
p [double1 doubleValue] == [float1 doubleValue] **// returns false**
If you compare them using isEqualToNumber
p [float1 isEqualToNumber:double1] **// returns false**
So if isEqualToNumber is going to return false, given that the creation of an NSNumber is a black box that may give you some other type on the way out, I'm not sure what good that method is.
So if you're going to make a test for dirty, because an existing value has been changed to a new value... what's the simplest way to do that that will handle all NSNumber comparisons.. not just float and double, but all NSNumbers?
It seems that converting to a string value, then compariing would be most useful, or perhaps a whole lot of extra code using NSNumberFormatter.
What are your thoughts?
It is not possible to reliably compare two IEEE floats or doubles. This has nothing to do with NSNumber. This is the nature of floating point. This is discussed in the context of simple C types at Strange problem comparing floats in objective-C. The only correct way to compare floating point numbers is by testing against a tolerance. I don't know what you mean by "fractional values are stripped off." Some digits are always lost in a floating point representation.
The particular test value you've provided demonstrates the problems quite nicely. 1.00001 cannot be expressed precisely in a finite number of binary digits. Wolfram Alpha is a nice way to explore this, but as a double, 1.00001 rounds to 1.0000100000000001. As a float, it rounds to 1.00001001. These numbers, obviously, are not equal. If you roundtrip them in different ways, it should not surprise you that isEqualToNumber: fails. This should make clear why your two floatValue calls do turn out to be equal. Rounded to the precision of float, they're "close enough."
If you want to compare floating point numbers, you must compare against an epsilon. Given recent advances in compiler optimization, even two identical pieces of floating point C code can generate slightly different values in their least-significant digits if you use -Ofast (we get big performance benefits by allowing that).
If you need some specific number of significant fractional digits, then it is usually better to work in fixed point notation. Just scale everything by the number of digits you need and work in integers. If you need floating point, but just want base-10 to work well (rather than base-2), then use NSDecimalNumber or NSDecimal. That will move your problems to things that round badly in base-10. But if you're working in floating point, you must deal with rounding errors.
For a much more extensive discussion, see "What Every Programmer Should Know About Floating-Point Arithmetic."

Why does calculation using real give different result from one using int?

I have this code for example:
(a) writeln ('real => ', exp(3*Ln(3)):0:0); // return 27
(b) writeln ('int => ', int(exp(3*Ln(3))):0:0); // return 26
Is a bug?
The function calc 3^3 (exponent using ln and exp function), but conversion from real to int fail; in case (a) return 27, in case (b) return (26), when should be 27 both.
As i can solve it?
Thanks very much for help.
Ps: Too assign result to integer variable, using trunc, result not change.
No, it is not a bug. Computers simply don't have infinite precision, so the result is not exactly 27, but perhaps 26.999999999 or something. And so, when you int or trunc it, it ends up as 26. Use Round instead.
The expression you're printing evaluates to something slightly less than 27 due to the usual floating-point errors. The computer cannot exactly represent the natural logarithm of 3, so any further calculations based on it will have errors, too.
In comments, you claim exp(3*ln(3)) = 27.000, but you've shown no programmatic evidence for that assertion. Your code says exp(3*ln(3)) = 27, which is less precise. It prints that because you explicitly told WriteLn to use less precision. The :0:0 part isn't just decoration. It means that you want to print the result with zero decimal places. When you tell WriteLn to do that, it rounds to that many decimal places. In this case, it rounds up. But when you introduce the call to Int, you truncate the almost-27 value to exactly 26, and then WriteLn trivially rounds that to 26 before printing it.
If you tell WriteLn to display more decimal places, you should see different results. Consult the documentation for Write for details on what the numbers after the colons mean.
Working with floating points doesn't always give a 100% exact result. The reason being is that binary floating points variable can't always represent values exactly. The same thing is true about decimal numbers. If you take 1/3, in a 6 digit precision decimal, would be 0.333333. Then if you take 0.333333 * 3 = 0.999999. Int(0.999999) = 0
Here is some litterature about it...
What Every Computer Scientist Should Know About Floating-Point Arithmetic
You should also take a look at Rudy Velthuis' article:
http://rvelthuis.de/articles/articles-floats.html
Not a bug. It is just yet another example of how floating arithmetic works on a computer. Floating point arithmetic is but an approximation of how the real numbers work in mathematics. There is no guarantee, and there can be no such guarantee, that floating point results will be infinitely accurate. In fact, you should expect them to almost always be imprecise to some degree.

Resources