Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misuse of Math library without strictfp #36

Closed
andelf opened this issue Oct 29, 2020 · 3 comments
Closed

Misuse of Math library without strictfp #36

andelf opened this issue Oct 29, 2020 · 3 comments

Comments

@andelf
Copy link
Contributor

andelf commented Oct 29, 2020

In java8, the default behavior of Math is using any available FPU like 8087. The results of float operations might be platform-dependent.
In later versions, the default behavior changes and will use IEEE-float(SSE2 instructions). So you can't update JDK/JRE without a proposal-fork.

In detail, java-tron's double arithmetic will use x87 instructions. And is difficult to port to other languages, since all modern languages use ieee-compatible and stable arithmetic for float numbers.

@andelf
Copy link
Contributor Author

andelf commented Oct 29, 2020

In ExchangeProcessor.java:

  private long exchangeToSupply(long balance, long quant) {
    long newBalance = balance + quant;
    double issuedSupply = -supply * (1.0 - Math.pow(1.0 + (double) quant / newBalance, 0.0005));
    // ...
}

The Math.pow(1.0 + (double) quant / newBalance, 0.0005) is the first step to calculate exchange amount.

let balance = 4732214, quant = 4732214, then:

Math.pow(1.0 + (double) quant / newBalance, 0.0005)
= Math.pow(1.0061363892207218, 0.0005)

= 1.000003058823805400000000 in java8, javatron.
But.
= 1.000003058823805100000000 in Python, Rust, java13, etc.

The final calculation result(exchange amount) in java8(mainnet) is 13516579896.

While in other languages, it's 13516579895. THIS IS A 1 SUN DIFF!!. 🐛

This is what happens in block #4137160, 0xa33ca08a8cffff532fa43771527e145a63a37fca50dd1468632acc146e8e2d7a.

@andelf
Copy link
Contributor Author

andelf commented Oct 29, 2020

To simulate the calculation in Rust:

Rust Playground

#![feature(asm)]

fn main() {
    let a = 1.0061363892207218_f64;
    let b = 0.0005_f64;

    let val0: f64 = a.powf(0.0005);
    let mut v = 0_f64;

    let mut cntr = 0_u16;
    unsafe {
        asm!(
            "fnstcw word ptr [{cntr}]",
            // calculate
            "fld qword ptr [{y}]",
            "fld qword ptr [{x}]",
            "fyl2x",
            "f2xm1",
            "fld1",
            "faddp st(1), st(0)",
            "fstp qword ptr [{v}]",
            x = in(reg) &a,
            y = in(reg) &b,
            v = in(reg) &mut v,
            cntr = in(reg) &mut cntr,
            options(nostack)
        );
    }

    println!("val0 => {}", val0);
    println!("val1 => {}", v);

    println!("high precision => {}", (cntr & 0x300) != 0); // high precision flag
}

Output

val0 => 1.0000030588238051
val1 => 1.0000030588238054
high precision => true

@andelf
Copy link
Contributor Author

andelf commented Oct 29, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant