SoFunction
Updated on 2025-04-12

Causes and solutions to iOS floating point type accuracy problem

Preface

I believe many people (actually I think it should be everyone) have encountered a problem, that is, when decimals appear in the JSON data returned by the server, the client will always experience accuracy loss when parsing with CGFloat, especially when encountering sensitive data, this accuracy loss is completely intolerable. This article will start from simple solutions and principles, and let you review this small problem that you have actually learned before but have forgotten almost.

How to solve the floating point precision problem

Rounding process

//For example, the server returns such a json{
    "price":1.9
}

// After the client parses the price and prints 1.9CGFloat price = 
NSLog(@"%f",price)
The result is 1.8999999999999999

At this time, as a salted fish developer, you can write the following code:

NSLog(@"%.2f",price)
Output:1.89
//Inaccurate enough, it doesn't matter, there is a wayNSLog(@"f%@",round(price*10)/10);
Output1.9

Of course, there is also the NSDecimalNumber type specially provided by Apple for accuracy issues that can also solve this problem. The usage of NSDecimalNumber is very simple. (As for how simple it is, please Baidu by yourself. Don’t ask me, I can’t write it even if I don’t have Baidu)

A better solution

  • So the question is, as a salted fish among the salted fish who is so lazy that they are reluctant to write articles for a whole year, which way should I choose to solve this problem?

OK, the highlight is here, next, let’s put the code!

//The server must return the string type. If the server does not do it: **Please take the hammer and gas tank to find the backend developer to solve the problem**@property (nonatomic,copy) NSString *price;

Yes, I will choose not to solve this problem and kick the ball out. This is what a qualified developer should do!

Causes of accuracy loss

Solving the floating point accuracy problem is one aspect, but such simple content is not enough to complete the entire article. So next, let’s talk about why it is inaccurate if it is well parsed?

Many people may be able to roughly tell that the accuracy is lost because of the floating point storage method. After all, this thing has been learned by the professional and appropriate tubes when they were in school, but everyone touched their little heads, hey, is it the same as me? Forgot all?

Moreover, since there are always some interviewers with awesome basics and those who have just reviewed this part of the content like to pick these small questions to make things difficult for us elderly programmers, let’s review this part of the knowledge.

How to store floating point types

The storage method of floating-point types in computers is stored in scientific notation:

For example: Scientific notation method to represent decimals
90.9 => 9.09 x 10^1
8.3  => 8.3 x 10^0

Let’s take 8.3 as an example. To store 8.3 (8.3 x 10^0), we must first convert 8.3 into binary, and 8 into binary 1000. So what about 0.3?

An elderly programmer, have you suddenly realized that you have forgotten how decimals are converted into 2? Put down your trembling hands that are ready for Baidu. I have everything you want here!

Taking 0.9 as an example, multiply 0.9 by 2, the obtained integer part of the number is the first bit of the binary decimal, take out the decimal part of the result part and multiply it by 2, take the integer part as the second bit, and repeat the above operation until the result is equal to 0 or a loop occurs.

0.9 *2 = 1.8  The1Bit 1
0.8 *2 = 1.6  The2Bit 1
0.6 *2 = 1.2  The3Bit 1
0.2 *2 = 0.4  The4Bit 0
0.4 *2 = 0.8  The5Bit 0
0.8 *2 = 1.6  The6Bit 1 A cycle has occurred

So0.9The binary is 0.1 1100 1100 1100 1100... in1100Infinite loop

After reviewing the above, we know that the binary system of 8.3 can be represented as 1000.0 1001 1001 1001... (1001 infinite loop) If expressed by scientific notation method, it is, 1.000 0 1001 1001 1001 x 2^3

Integer part Index section Decimal Part
1 3 .000 0 1001 1001 1001

And we know that float is 4 (32 bits) bytes, and each bit of its is allocated as follows when storing

31st bit sign bit 23-30 bits (exponential bits) 0-22 digits (decimal places)
1 3 .000 0 1001 1001 1001

Number of significant digits

You can see that there are 23 digits in the mantissa part, but since it is a scientific and technological method, any number can be used. x 2^bIn this form, 1 can be omitted (this time someone will argue, why? x 2^bWhat to do if this is the case! The answer is: b can be a negative number), then the number of bits that a total of 23 bits of data can actually be expressed is actually 24 bits.

  • The maximum number that can be expressed by 24 bits is 16777215 , and if it is greater than this number, it cannot be expressed accurately.

Of course, it is not necessarily impossible to express exactly if it is greater than 16777215. For example, 16777216, its binary expression is 1 with 1 heap 0 after 2. Because it is an integer power of 2, then all the following are 0. Therefore, 23 bits can store the number. If 1 appears after 23 bits, it will not work. And so on, 16777215 will have numbers that just meet the conditions of integer power of 2, and can also be expressed correctly.

Although there are some numbers greater than 16777215 that can be expressed accurately, if we talk about accuracy, we must be accurate. Then the numbers that can be expressed accurately are only between 0-16777215. Let's count between 16777215, and there are 8 bits in total, but since the highest bit 1 cannot be included in all, the accuracy should be a 7-bit significant number.

Also, let’s talk about the expression range of floating point numbers. This range must be determined by the index. I won’t forget how much it is. If you are interested, you can calculate it.

Index storage method: shift storage

You can see that the exponent part is given a total of 8 bits. Since the exponent has positive and negative, then assuming that our first bit represents the sign bit, the range of numbers we can represent is -127 ~ +127

Then the range that can be represented is divided into two parts:

  • 1 000000 0~ 1 111111 1 => -0 to -127

  • 0 000000 0~ 0 111111 1 => +0 to 127

Obviously, if this happens, a -0 and a +0 will appear. In order to avoid this problem, shift storage occurs: that is, if the highest bit is not used to represent the sign bit, the range that can be stored by 8 bits is 0-255. Let's re-plan the next two intervals:

  • 00000000 - 01111111 => 0-127 Subtracting 127 means the number before -127-0
  • 10000000 - 11111111 => 128-256 Subtracting 127 means the number before 1-127

This avoids the problem of +-0. The above-mentioned 8.3 is converted into a decimal and the exponential digit is 3. Then the actual storage time of 3 is 130, which is 10000010. Let’s update the actual storage table.

31st bit sign bit 23-30 bits (exponential bits) 0-22 digits (decimal places)
1 10000010 .000 0 1001 1001 1001

Double type

The double type is 8 bytes and 64 bits. The number of bits it represents is different from the float. The others are the same. The number of bits represented by double precision is as follows:

63rd bit sign bit 52-62 bits (exponential bits) 0-51 digits (decimal places)

Summary: The reason for the output result to lose accuracy

Back to the original number 1.9 is printed as 1.899999999999999999. Now we should know why, because 1.9 is an infinite loop when it is turned into 2000. Due to storage reasons, the subsequent loop part is only intercepted by 23 bits, and the restored decimal number is definitely different from the original one. Similarly, if the decimal point is followed by a decimal of multiples of 5, such as .5, the printed decimal will generally be normal, because .5 is not an infinite loop when it turns to 2.

This is the end of this article about the causes and solutions to the iOS floating point type accuracy problem. For more related content on iOS floating point type accuracy problem, please search for my previous articles or continue browsing the following related articles. I hope everyone will support me in the future!