Describe the bug
Conversion of large positive f64
numbers to u128
fails with e.g.:
- Doing
u128.fromF64(value)
fails with error: FunctionCallError(WasmTrap(IllegalArithmetic))
- Doing
u128.fromString(value.toString())
fails by converting to a simple digit number (does not take into the fraction nor exponent).
When the f64
value can be represented with an exponent e.g. 2.5e+23
To Reproduce
i have deployed the following smart contract to the following account f64-to-128.cristobal.testnet
:
Small float values to u128 (OK)
Conversion for small values works fine e.g.:
export function small_values_ok():u128[] {
return [
u128.fromF64(parseFloat('10') as f64),
u128.fromF64(parseFloat('100') as f64),
u128.fromF64(parseFloat('1000') as f64),
u128.fromF64(parseFloat('10000') as f64),
u128.fromF64(parseFloat('100000') as f64),
u128.fromF64(parseFloat('10000000') as f64)
]
}
Result from call:
![Screenshot 2021-12-02 at 14 46 31](https://user-images.githubusercontent.com/155505/144434054-ad2a61f6-1122-4b98-89c1-03462155cc43.png)
Large value float value to u128 (Fails):
Conversion from f64
value fails e.g:
export function large_values_from_f64_fails(): u128[] {
return [
u128.fromF64(
parseFloat('1000000000000000000000000') as f64
)
]
}
![Screenshot 2021-12-02 at 14 56 13](https://user-images.githubusercontent.com/155505/144435689-46e9bba2-20d6-4ebb-9d97-05b56d83c46b.png)
Conversion from f64
string value also fails e.g.:
export function large_values_from_f64_string_fails(): u128[] {
return [
u128.fromString(
(parseFloat('1000000000000000000000000') as f64).toString()
)
]
}
![Screenshot 2021-12-02 at 14 56 33](https://user-images.githubusercontent.com/155505/144435748-802c821d-8c3e-48b9-8cb1-022b09494cdf.png)
Large values works when expanding string to include trailing zeros.
Created a custom function that expands large float values that are represented with exponent e.g.:
function parseF64ValueToString(value: f64): string {
const repr = value.toString();
if (repr.indexOf('e+') < 0) {
return repr;
}
const elements = repr.split('e+');
const val = elements[0];
const args = [
val.charAt(0)
];
let stop = val.indexOf('0') > 0
? val.indexOf('0')
: val.length;
for (let i = 2; i < stop; i++) {
args.push(val.charAt(i));
}
const exp = parseInt(elements[1]) as i32;
stop = exp - (args.length - 1);
for (let i = 0; i < stop; i++) {
args.push('0');
}
return args.join('');
}
This is how the values are represented without expansion:
![Screenshot 2021-12-02 at 15 01 46](https://user-images.githubusercontent.com/155505/144436663-baef8a92-f273-42e1-94e6-ffb537343b14.png)
This is how the values are represented with expansion:
![Screenshot 2021-12-02 at 15 01 32](https://user-images.githubusercontent.com/155505/144436637-269ae291-c466-48ab-ae79-9eec6bc5bfee.png)
We can see that converting the expanded functions works fine:
export function large_values_ok(): u128[] {
return [
u128.fromString(
parseF64ValueToString(
parseFloat('1000000000000000000000000') as f64
)
),
u128.fromString(
parseF64ValueToString(
parseFloat('2500000000000000000000000') as f64
)
)
]
}
![Screenshot 2021-12-02 at 14 58 02](https://user-images.githubusercontent.com/155505/144436003-c0a082be-ab5b-4d93-941e-90c3de7b5b16.png)
Expected behavior
Would expect that conversion for large float values to u128 should work as expected.
Perhaps the approach above could be a temporary solution until a proper approach is in place, i am no expert on web assembly and not sure how this should be done there.
Maybe some logic like this could be used here:
@inline
static fromF64(value: f64): u128 {
if (value.toString().indexOf('e+')) {
return fromString(parseF64ValueToString(value));
}
return new u128(<u64>value, reinterpret<i64>(value) >> 63);
}