Why does Rust's floating point division work differently than equivalent C++ code?
Asked Answered
R

0

6

Any calculations x / y (where both operands are 32bit floats) yield 0 in Rust when I compile for the AVR ISA. Specifically, I use avr_hal for interacting with the Arduino. I read unsigned integer values, convert them to floating point numbers, divide to get a relative quantity, and multiply with another number (such that f*(x/y) > 1) and cast back to an unsigned integer:

let mut read_value: u16 = 0;
loop {
  read_value = pot.analog_read(&mut adc); // between 0 and 1023, potentiometer input
  let relative: f32 = (read_value as f32) / 1023.0; // between 0.0 and 1.0
  let v: f32 = relative * 1800.0; // > 1.0 in most cases
  ufmt::uwriteln!("{}", v as u16); // always 0
}

This problem doesn't seem to occur with seemingly equivalent C++ code:

float read_value = 0;
void loop() {
    read_value = analogRead(A0);
    float relative = read_value / 1023.0f;
    float v = relative * 1000.0;
    Serial.println(static_cast<int>(v));
}

What exactly is this Rust-quirk?

Rattlebrain answered 21/2, 2022 at 12:0 Comment(12)
Maybe conversion from f32 to u16 doesn't work properly? Have you tried u32 instead? Converting floats directly to shorts is quite a rare operation.Dede
yes, I have tried a plethora of alternative types and all of them showed the expected behavior in regular rust (x86 i guess), while none of them worked on the Arduino. Looking at the official map implementation, I see that they avoid floating point arithmetic entirely, which suggests that this is indeed due to the architecture.Rattlebrain
I've never tried rust on the Arduino (nor anywhere else, but that doesn't matter), but floating point math does normally work just fine. So it's not true that the AVR is not able to do any floating point in general.Dede
I have found multiple sources that explain how division on AVR happens since there is no instruction in the ISA that handles it. And I haven't found a "32 bit floating point division" assembly routine that would hint that it's actually supported.Rattlebrain
AVR doesn't support floating point operations by hardware. This is all software supported, thus depends on your framework. Maybe the AVR Rust compiler is not support floating point divisions.Belenbelesprit
Floating point on AVR is often avoided, as it has two big caveats: 1. Single precision only 2. It is quite slow (because it's all done in software). Other than that, it works fine, and does follow the IEEE-754 standard.Mede
@chrisi it uses gcc-avr and I could only find signed and unsigned 8 and 16 bit integer division routines, nothing for floats.Rattlebrain
@edgar-bonet would you claim the single-precision aspect explains why I always get 0?Rattlebrain
No. I claim that, other than these two caveats, floating point works fine on C++. I don't know what's wrong with the Rust floating point implementation.Mede
Write the same code in C++ (it's very simple) and see it work. If that is the case, then there is something wrong with Rust. We cannot help you with rust.Barris
I hate myself right about now, I could swear I tried exactly that, and it didn't work. So the original question stands.Rattlebrain
This seems to be a known issue: github.com/rust-lang/rust/issues/108489Caputo

© 2022 - 2024 — McMap. All rights reserved.