mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Fix hashing of equal decimals with different scales (#5179)
Co-authored-by: Laurenz <laurmaedje@gmail.com>
This commit is contained in:
parent
b5b92e21e9
commit
16736feb13
@ -1,4 +1,5 @@
|
|||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::Neg;
|
use std::ops::Neg;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
@ -88,7 +89,7 @@ use crate::World;
|
|||||||
/// to rounding. When those two operations do not surpass the digit limits, they
|
/// to rounding. When those two operations do not surpass the digit limits, they
|
||||||
/// are fully precise.
|
/// are fully precise.
|
||||||
#[ty(scope, cast)]
|
#[ty(scope, cast)]
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Decimal(rust_decimal::Decimal);
|
pub struct Decimal(rust_decimal::Decimal);
|
||||||
|
|
||||||
impl Decimal {
|
impl Decimal {
|
||||||
@ -370,6 +371,22 @@ impl Neg for Decimal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Hash for Decimal {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
// `rust_decimal`'s Hash implementation normalizes decimals before
|
||||||
|
// hashing them. This means decimals with different scales but
|
||||||
|
// equivalent value not only compare equal but also hash equally. Here,
|
||||||
|
// we hash all bytes explicitly to ensure the scale is also considered.
|
||||||
|
// This means that 123.314 == 123.31400, but 123.314.hash() !=
|
||||||
|
// 123.31400.hash().
|
||||||
|
//
|
||||||
|
// Note that this implies that equal decimals can have different hashes,
|
||||||
|
// which might generate problems with certain data structures, such as
|
||||||
|
// HashSet and HashMap.
|
||||||
|
self.0.serialize().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A value that can be cast to a decimal.
|
/// A value that can be cast to a decimal.
|
||||||
pub enum ToDecimal {
|
pub enum ToDecimal {
|
||||||
/// A string with the decimal's representation.
|
/// A string with the decimal's representation.
|
||||||
@ -386,3 +403,27 @@ cast! {
|
|||||||
v: f64 => Self::Float(v),
|
v: f64 => Self::Float(v),
|
||||||
v: Str => Self::Str(EcoString::from(v)),
|
v: Str => Self::Str(EcoString::from(v)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use super::Decimal;
|
||||||
|
use crate::utils::hash128;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decimals_with_equal_scales_hash_identically() {
|
||||||
|
let a = Decimal::from_str("3.14").unwrap();
|
||||||
|
let b = Decimal::from_str("3.14").unwrap();
|
||||||
|
assert_eq!(a, b);
|
||||||
|
assert_eq!(hash128(&a), hash128(&b));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decimals_with_different_scales_hash_differently() {
|
||||||
|
let a = Decimal::from_str("3.140").unwrap();
|
||||||
|
let b = Decimal::from_str("3.14000").unwrap();
|
||||||
|
assert_eq!(a, b);
|
||||||
|
assert_ne!(hash128(&a), hash128(&b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -31,6 +31,13 @@
|
|||||||
// Error: 10-19 float is not a valid decimal: float.nan
|
// Error: 10-19 float is not a valid decimal: float.nan
|
||||||
#decimal(float.nan)
|
#decimal(float.nan)
|
||||||
|
|
||||||
|
--- decimal-scale-is-observable ---
|
||||||
|
// Ensure equal decimals with different scales produce different strings.
|
||||||
|
#let f1(x) = str(x)
|
||||||
|
#let f2(x) = f1(x)
|
||||||
|
#test(f2(decimal("3.140")), "3.140")
|
||||||
|
#test(f2(decimal("3.14000")), "3.14000")
|
||||||
|
|
||||||
--- decimal-repr ---
|
--- decimal-repr ---
|
||||||
// Test the `repr` function with decimals.
|
// Test the `repr` function with decimals.
|
||||||
#test(repr(decimal("12.0")), "decimal(\"12.0\")")
|
#test(repr(decimal("12.0")), "decimal(\"12.0\")")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user