Skip to content

Commit 5445aef

Browse files
committed
Auto merge of #17558 - beetrees:fix-double-rounding, r=Veykril
fix: Fix double rounding of `f32` literals Fixes #17556 by delaying parsing until the type is known. Also adds a test to check the issue is fixed.
2 parents fa29aa3 + da41ae7 commit 5445aef

File tree

4 files changed

+47
-33
lines changed

4 files changed

+47
-33
lines changed

src/tools/rust-analyzer/crates/hir-def/src/hir.rs

+13-16
Original file line numberDiff line numberDiff line change
@@ -56,29 +56,29 @@ pub struct Label {
5656
}
5757
pub type LabelId = Idx<Label>;
5858

59-
// We convert float values into bits and that's how we don't need to deal with f32 and f64.
60-
// For PartialEq, bits comparison should work, as ordering is not important
59+
// We leave float values as a string to avoid double rounding.
60+
// For PartialEq, string comparison should work, as ordering is not important
6161
// https://github.com/rust-lang/rust-analyzer/issues/12380#issuecomment-1137284360
62-
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
63-
pub struct FloatTypeWrapper(u64);
62+
#[derive(Default, Debug, Clone, Eq, PartialEq)]
63+
pub struct FloatTypeWrapper(Box<str>);
6464

6565
impl FloatTypeWrapper {
66-
pub fn new(value: f64) -> Self {
67-
Self(value.to_bits())
66+
pub fn new(value: String) -> Self {
67+
Self(value.into())
6868
}
6969

70-
pub fn into_f64(self) -> f64 {
71-
f64::from_bits(self.0)
70+
pub fn to_f64(&self) -> f64 {
71+
self.0.parse().unwrap_or_default()
7272
}
7373

74-
pub fn into_f32(self) -> f32 {
75-
f64::from_bits(self.0) as f32
74+
pub fn to_f32(&self) -> f32 {
75+
self.0.parse().unwrap_or_default()
7676
}
7777
}
7878

7979
impl fmt::Display for FloatTypeWrapper {
8080
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81-
write!(f, "{:?}", f64::from_bits(self.0))
81+
f.write_str(&self.0)
8282
}
8383
}
8484

@@ -120,10 +120,7 @@ impl From<ast::LiteralKind> for Literal {
120120
match ast_lit_kind {
121121
LiteralKind::IntNumber(lit) => {
122122
if let builtin @ Some(_) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
123-
Literal::Float(
124-
FloatTypeWrapper::new(lit.float_value().unwrap_or(Default::default())),
125-
builtin,
126-
)
123+
Literal::Float(FloatTypeWrapper::new(lit.value_string()), builtin)
127124
} else if let builtin @ Some(_) = lit.suffix().and_then(BuiltinUint::from_suffix) {
128125
Literal::Uint(lit.value().unwrap_or(0), builtin)
129126
} else {
@@ -133,7 +130,7 @@ impl From<ast::LiteralKind> for Literal {
133130
}
134131
LiteralKind::FloatNumber(lit) => {
135132
let ty = lit.suffix().and_then(BuiltinFloat::from_suffix);
136-
Literal::Float(FloatTypeWrapper::new(lit.value().unwrap_or(Default::default())), ty)
133+
Literal::Float(FloatTypeWrapper::new(lit.value_string()), ty)
137134
}
138135
LiteralKind::ByteString(bs) => {
139136
let text = bs.value().map_or_else(|_| Default::default(), Box::from);

src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1432,8 +1432,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
14321432
hir_def::hir::Literal::Int(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),
14331433
hir_def::hir::Literal::Uint(it, _) => Box::from(&it.to_le_bytes()[0..size()?]),
14341434
hir_def::hir::Literal::Float(f, _) => match size()? {
1435-
8 => Box::new(f.into_f64().to_le_bytes()),
1436-
4 => Box::new(f.into_f32().to_le_bytes()),
1435+
8 => Box::new(f.to_f64().to_le_bytes()),
1436+
4 => Box::new(f.to_f32().to_le_bytes()),
14371437
_ => {
14381438
return Err(MirLowerError::TypeError("float with size other than 4 or 8 bytes"))
14391439
}

src/tools/rust-analyzer/crates/ide/src/hover/tests.rs

+22
Original file line numberDiff line numberDiff line change
@@ -5310,6 +5310,28 @@ const FOO$0: f64 = expf64(1.2);
53105310
```
53115311
"#]],
53125312
);
5313+
// check `f32` isn't double rounded via `f64`
5314+
check(
5315+
r#"
5316+
/// This is a doc
5317+
const FOO$0: f32 = 1.9999999403953552_f32;
5318+
"#,
5319+
expect![[r#"
5320+
*FOO*
5321+
5322+
```rust
5323+
test
5324+
```
5325+
5326+
```rust
5327+
const FOO: f32 = 1.9999999
5328+
```
5329+
5330+
---
5331+
5332+
This is a doc
5333+
"#]],
5334+
);
53135335
}
53145336

53155337
#[test]

src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs

+10-15
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
//! There are many AstNodes, but only a few tokens, so we hand-write them here.
22
3-
use std::{
4-
borrow::Cow,
5-
num::{ParseFloatError, ParseIntError},
6-
};
3+
use std::{borrow::Cow, num::ParseIntError};
74

85
use rustc_lexer::unescape::{
96
unescape_byte, unescape_char, unescape_mixed, unescape_unicode, EscapeError, MixedUnit, Mode,
@@ -393,9 +390,9 @@ impl ast::IntNumber {
393390
}
394391
}
395392

396-
pub fn float_value(&self) -> Option<f64> {
393+
pub fn value_string(&self) -> String {
397394
let (_, text, _) = self.split_into_parts();
398-
text.replace('_', "").parse::<f64>().ok()
395+
text.replace('_', "")
399396
}
400397
}
401398

@@ -432,14 +429,9 @@ impl ast::FloatNumber {
432429
}
433430
}
434431

435-
pub fn value(&self) -> Result<f64, ParseFloatError> {
436-
let (text, _) = self.split_into_parts();
437-
text.replace('_', "").parse::<f64>()
438-
}
439-
440-
pub fn value_f32(&self) -> Result<f32, ParseFloatError> {
432+
pub fn value_string(&self) -> String {
441433
let (text, _) = self.split_into_parts();
442-
text.replace('_', "").parse::<f32>()
434+
text.replace('_', "")
443435
}
444436
}
445437

@@ -509,10 +501,13 @@ mod tests {
509501

510502
fn check_float_value(lit: &str, expected: impl Into<Option<f64>> + Copy) {
511503
assert_eq!(
512-
FloatNumber { syntax: make::tokens::literal(lit) }.value().ok(),
504+
FloatNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f64>().ok(),
505+
expected.into()
506+
);
507+
assert_eq!(
508+
IntNumber { syntax: make::tokens::literal(lit) }.value_string().parse::<f64>().ok(),
513509
expected.into()
514510
);
515-
assert_eq!(IntNumber { syntax: make::tokens::literal(lit) }.float_value(), expected.into());
516511
}
517512

518513
fn check_int_value(lit: &str, expected: impl Into<Option<u128>>) {

0 commit comments

Comments
 (0)