Calculator disaster: how much is 10%+10%?

  • We humans think it’s 0.2, but what about turning on the phone calculator?

decryption

A single – step method used by foreign computing programs. So a plus b% is a times 1 plus b%. So, the phone calculator is actually calculating 10%* (1+10%) = 0.11.

Let’s just say a little bit more about how it works. Take 8+10% for example, why =8.8 and not 8.1? 8 yuan plus a 10% tip makes a total of 8.8 yuan.

The first electronic calculators did not have %, they were added later. As a follow-up, it must address common pain points in computing scenarios, rather than being brain-dead. I guess it’s probably a common scenario for Westerners to calculate discounts, tips, interest, etc.

2 The devastated Double

  • Four floating-point operations

  • The results of

Since computers store values internally in binary, so do floating point numbers. Java uses IEEE 754 standard to realize floating point number expression and operation. For example, the binary representation of 0.1 is 0.0 0011 0011 0011… (0011) infinite loop, and then converted to a decimal is 0.1000000000000000055511151231257827021181583404541015625. The computer could not accurately represent 0.1, so floating-point calculations caused a loss of accuracy.

You might think, for example, that 0.1, converted from decimal to binary, is too small to have a serious impact on computing. However, a lot of double is used for a lot of money calculation, and the final loss of precision is a lot of money in and out.

A hacker took advantage of a banking loophole to steal more than $50,000 from PayPal, Google Checkout and other online payment companies, stealing just a few cents at a time. The loophole he exploited was that the bank would send a small amount of money, typically a few cents to a few dollars, after opening an account to verify that the account was valid. Google Checkout and Paypal use the same method to verify credit and debit card accounts bundled with online accounts. Opened 58,000 accounts with an automated script, collected thousands of micropayments and deposited them into several personal bank accounts. Scammed over $8,000 in cash from Google Checkout. The bank noticed the strange cash flow and got in touch. Largent explained that he had read the terms of service carefully, believed he had done nothing wrong, and said he needed the money to pay off the debt. But Largent used fake names, including the names of cartoon characters, fake addresses and social Security numbers, thus violating mail, banking and wire fraud laws. Don’t try it in China. It’s a life sentence.

3 The Saved BigDecimal

We know that BigDecimal, in scenarios where floating-point numbers are accurately expressed and evaluated, must be used. However, there are several pits to avoid when using BigDecimal.

  • The four previous operations for BigDecimal

  • The output

The result is still not precise, but it is more precise.

3.1 BigDecimal represents/evaluates floating point numbers and uses a string constructor

  • Perfect output

How do I convert an exact representation of BigDecimal to a Double when I can’t call the constructor that BigDecimal passed in for a Double?

  • Double.toStringIs it possible to convert a double to a string?

  • The output

401.5000. Why is there an extra 0 compared to the 401.500 obtained by multiplying the string initializer 100 and 4.015 above? BigDecimal has the number of digits to the right of the decimal point -precision, which is the length of the significant number

New BigDecimal(double.toString (100)) = scale=1, precision=4; New BigDecimal(” 100 “) results in A BigDecimal with scale=0 and precision=3.

A BigDecimal multiplication operation that returns a value whose scale is the sum of the scale of two numbers. So, two different ways of initializing 100 result in scales of 4 and 3 respectively:

private static void testScale(a) {
    BigDecimal bigDecimal1 = new BigDecimal("100");
    BigDecimal bigDecimal2 = new BigDecimal(String.valueOf(100d));
    BigDecimal bigDecimal3 = new BigDecimal(String.valueOf(100));
    BigDecimal bigDecimal4 = BigDecimal.valueOf(100d);
    BigDecimal bigDecimal5 = new BigDecimal(Double.toString(100));

    print(bigDecimal1); //scale 0 precision 3 result 401.500
    print(bigDecimal2); // Scale 1 precision 4 result 401.5000
    print(bigDecimal3); //scale 0 precision 3 result 401.500
    print(bigDecimal4); // Scale 1 precision 4 result 401.5000
    print(bigDecimal5); // Scale 1 precision 4 result 401.5000
}

private static void print(BigDecimal bigDecimal) {
    log.info("scale {} precision {} result {}", bigDecimal.scale(), bigDecimal.precision(), bigDecimal.multiply(new BigDecimal("4.015")));
}
Copy the code

Rounding and formatting of floating point numbers

Explicit coding should be considered, through formatted expressions or formatting tools

4.1 clearDecimal digitsandRounding mode

  • In string. format%.1fFormat a 3.35 float for double/float

  • The results of

3.4 and 3.3

Precision issues and rounding result in: the 3.35 actual storage representation of double/float

3.350000000000000088817841970012523233890533447265625
3.349999904632568359375
Copy the code

Format is rounded to 1 decimal place, with 3.350 for double rounded to 3.4 and 3.349 for float rounded to 3.3.

If we look at the source code for the Formatter class, we can see that the rounding mode used is HALF_UP (line 11) :

If you want to use otherRounding mode, can be installedDecimalFormat

When rounded down by two decimal places, the output is 3.35 and 3.34, respectively, because floating-point numbers cannot be stored exactly.

So even with DecimalFormat controlling rounding exactly, double/float can produce strange results, so

4.2 String formatting also uses BigDecimal

  • BigDecimal formats the number 3.35 by rounding down and rounding off 1 decimal place, respectively

  • The results of

3.3 and 3.4, as expected.

Best practice: You should use BigDecimal to represent, evaluate, and format floating point numbers.

5 Equals must be right?

Wrapper classes are compared over equals, not ==. Does using Equals to evaluate two BigDecimal numbers necessarily meet expectations?

  • useequalsCompare 1.0 and 1 BigDecimal:

The result, of course, is false. BigDecimal’s equals compares BigDecimal’svalueandscale: 1.0 scale is 1,1 scale is 0, so the result is false

To compare only the values of BigDecimal, use thecompareTo

The BigDecimalequalsandhashCodeWill consider bothvalueandscaleHashSet/HashMap may cause problems. Add a BigDecimal with a value of 1.0 to the HashSet and determine if there is a BigDecimal with a value of 1, resulting in false

5.1 Solutions

5.1.1 Replacing a HashSet with TreeSet

TreeSet don’t usehashCode, and do not useequalsCompare elements while usingcompareToMethods.

5.1.2 Remove the tail zero

Before storing BigDecimal into a HashSet or HashMap, use the stripTrailingZeros method to remove the trailing zeros. Also remove the trailing 0 when comparing to make sure that values of the same BigDecimal and scales are the same:

6 Overflow Problem

All basic numeric types have the possibility of being out of save scope.

  • Plus 1 Max for Long

  • The result is a negative number, and the maximum value of Long +1 becomes the minimum value of Long

– 9223372036854775808.

Obviously an overflow has occurred and no exceptions have been thrown.

6.1 Solution

6.1.1 Using xxExact of the Math class to Perform Numerical Calculation

These methods actively throw exceptions when values overflow.Upon execution, we get ArithmeticException, which is a RuntimeException:

java.lang.ArithmeticException: long overflow
Copy the code

6.1.2 Using BigInteger

BigDecimal specializes in experts dealing with floating point numbers, while BigInteger specializes in scientific calculations of large numbers.

  • Use BigInteger to +1 the maximum value of Long. To convert the result to Long, use BigInteger#longValueExact, which also throws ArithmeticException when the conversion overruns

  • The results of
9223372036854775808
java.lang.ArithmeticException: BigInteger out of long range
Copy the code

Incrementing the maximum value of Long by BigInteger is fine, but an overflow message is prompted when the result is converted to Long.

reference

  • www.eet-china.com/news/201909…