Skip to content

add scalbn and ilogb methods on floats - #163187

Draft
folkertdev wants to merge 1 commit into
rust-lang:mainfrom
folkertdev:scalbn
Draft

folkertdev wants to merge 1 commit into
rust-lang:mainfrom
folkertdev:scalbn

Conversation

@folkertdev

Copy link
Copy Markdown
Contributor

tracking issue: #163184

@rustbot rustbot added A-compiler-builtins Area: compiler-builtins (https://github.com/rust-lang/compiler-builtins) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Sep 22, 2026
Comment thread library/core/src/num/f32.rs Outdated
Comment thread library/coretests/tests/num/floats.rs
Comment thread library/coretests/tests/num/floats.rs Outdated
Comment thread library/core/src/intrinsics/mod.rs
}

intrinsic_dispatch_on_type! {
/// Returns the order of magnitude of `x` (in base 2).

@clarfonthey clarfonthey Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably clarify that this means it ignores the sign, even though it's technically correct.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a concrete suggestion for how to rephrase this? When I try it just gets more confusing, leaning on the examples here seems fine to me.

Comment on lines +1685 to +1694
/// Placeholder.
#[inline]
#[unstable(feature = "float_exponent_methods", issue = "163184")]
#[rustc_const_unstable(feature = "float_exponent_methods", issue = "163184")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn scalbn(self, exponent: i32) -> f128 {
intrinsics::scalbn(self, exponent)
}

/// Placeholder.

@clarfonthey clarfonthey Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Placeholder docs. (I hate that we can't just use macros to define the float types.)

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What could be nice nowadays is putting the docs into one of the macro_rules! attribute macros so you could do something like #[float_docs(ilogb, f128, constant_a = 1234, constant_b = 3.141)]. So the docs can get reused but "go to source" works correctly and the implementations can deviate where necessary.

Comment on lines +1671 to +1680
/// Placeholder.
#[inline]
#[unstable(feature = "float_exponent_methods", issue = "163184")]
#[rustc_const_unstable(feature = "float_exponent_methods", issue = "163184")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn scalbn(self, exponent: i32) -> f16 {
intrinsics::scalbn(self, exponent)
}

/// Placeholder.

@clarfonthey clarfonthey Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Placeholder docs. (I hate that we can't just use macros to define the float types.)

View changes since the review

intrinsics::scalbn(self, exponent)
}

/// Returns the exponent of `self`.

@clarfonthey clarfonthey Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably clarify that it's the absolute value of self, and ignores sign.

View changes since the review

Comment on lines +1806 to +1815
/// Placeholder.
#[inline]
#[unstable(feature = "float_exponent_methods", issue = "163184")]
#[rustc_const_unstable(feature = "float_exponent_methods", issue = "163184")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn scalbn(self, exponent: i32) -> f64 {
intrinsics::scalbn(self, exponent)
}

/// Placeholder.

@clarfonthey clarfonthey Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Placeholder docs. (I hate that we can't just use macros to define the float types.)

View changes since the review

Comment on lines +1243 to +1245
// bitwise, no NaN adjustments
sym::fabs => interp_ok(x.abs().into()),
sym::ilogb => interp_ok(Scalar::from_i32(x.ilogb())),

@RalfJung RalfJung Sep 25, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment seems wrong for the new intrinsic -- I think ilogb needs NaN and precision non-determinism.

View changes since the review

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be exact: it just extracts the exponent, accounting for subnormals, and returns an int.

The weird thing about it is that the ilogb libm function returns either INT_MIN or -INT_MAX for an input of 0, INT_MAX or INT_MIN for NaN. I guess that nondeterminism wouldn't hurt to add.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// bitwise, no NaN adjustments
sym::fabs => interp_ok(x.abs().into()),
sym::ilogb => interp_ok(Scalar::from_i32(x.ilogb())),
// fabs and ilogb are both bitwise, no NaN adjustments
sym::fabs => interp_ok(x.abs().into()),
sym::ilogb => interp_ok(Scalar::from_i32(x.ilogb())),

Comment on lines +1261 to +1263
let res = arg.scalbn(exp);
let res = self.adjust_nan(res, &[arg]);
interp_ok(res.into())

@RalfJung RalfJung Sep 25, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't doing precision non-determinism. That seems wrong?

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also a bitwise operation, so the result is exact.

@RalfJung RalfJung Sep 25, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are three kinds of float operations:

  • bitwise ops like fabs, fully deterministic
  • core arithmetic operations like add, mul, div, sqrt; non-deterministic NaNs but otherwise deterministic, precise results
  • imprecise arithmetic operations like sin, exp

What does scalbn guarantee about NaNs? If it's bitwise, it should preserve the exact NaN payload and sign. Your implementation calls adjust_nan indicating that it is not bitwise.

What's the best authoritative spec for scalbn (and ilogb)?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven’t read what exactly the C spec does but most implementations prepare a scale based on n then multiply x by it. So possible results are the same as multiplication between the input and a real, at least in practice.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exactly authoritative but a data point

https://en.cppreference.com/c/numeric/math/scalbn

Return value

If no errors occur, [ ... ]

If a range error due to overflow occurs, ±HUGE_VAL, ±HUGE_VALF, or ±HUGE_VALL is returned.

If a range error due to underflow occurs, the correct result (after rounding) is returned.

My interpretation of that is that it's in this category

core arithmetic operations like add, mul, div, sqrt; non-deterministic NaNs but otherwise deterministic, precise results

Nothing suggests it's in the sin etc category. And modulo underflow it is an exact operation, except that it's not guaranteed that a NaN is returned unchanged, hence the adjust_nan.

@tgross35 tgross35 left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Poor Folkert, just a draft PR and already there's so much feedback :) sorry to pile on here

View changes since this review

Comment on lines 21 to +47
(
$(#[$attr:meta])*
$vis:vis fn $name:ident<$generic:ident: $bound:path>(
$($arg:ident: $arg_ty:ty),* $(,)?
) -> $ret_ty:ty;
$($concrete:ty => $body:block)*
) => {
intrinsic_dispatch_on_type! {
@declare [] $(#[$attr])*
$vis fn $name<$generic: $bound>($($arg: $arg_ty),*) -> $ret_ty;
$($concrete => $body)*
}
};

(
$(#[$attr:meta])*
$vis:vis const fn $name:ident<$generic:ident: $bound:path>(
$($arg:ident: $arg_ty:ty),* $(,)?
) -> $ret_ty:ty;
$($concrete:ty => $body:block)*
) => {
intrinsic_dispatch_on_type! {
@declare [const] $(#[$attr])*
$vis fn $name<$generic: $bound>($($arg: $arg_ty),*) -> $ret_ty;
$($concrete => $body)*
}
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can avoid the duplicate with $(const $($const_dummy:literal)?)? and emitting $(const $($const_dummy)?)?. That's annoying to write so idk if it's worth it, but it works better if you might need to stack const/unsafe/async/etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can change that when it's needed.

Comment on lines +1243 to +1245
// bitwise, no NaN adjustments
sym::fabs => interp_ok(x.abs().into()),
sym::ilogb => interp_ok(Scalar::from_i32(x.ilogb())),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be exact: it just extracts the exponent, accounting for subnormals, and returns an int.

The weird thing about it is that the ilogb libm function returns either INT_MIN or -INT_MAX for an input of 0, INT_MAX or INT_MIN for NaN. I guess that nondeterminism wouldn't hurt to add.

Comment on lines +1685 to +1694
/// Placeholder.
#[inline]
#[unstable(feature = "float_exponent_methods", issue = "163184")]
#[rustc_const_unstable(feature = "float_exponent_methods", issue = "163184")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub const fn scalbn(self, exponent: i32) -> f128 {
intrinsics::scalbn(self, exponent)
}

/// Placeholder.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What could be nice nowadays is putting the docs into one of the macro_rules! attribute macros so you could do something like #[float_docs(ilogb, f128, constant_a = 1234, constant_b = 3.141)]. So the docs can get reused but "go to source" works correctly and the implementations can deviate where necessary.

Comment thread library/core/src/num/f128.rs Outdated
@rust-bors

rust-bors Bot commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

☔ The latest upstream changes (presumably #164788) made this pull request unmergeable. Please resolve the merge conflicts by rebasing.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-compiler-builtins Area: compiler-builtins (https://github.com/rust-lang/compiler-builtins) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants