conv.rs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. use testcrate::*;
  2. macro_rules! i_to_f {
  3. ($($from:ty, $into:ty, $fn:ident);*;) => {
  4. $(
  5. fuzz(N, |x: $from| {
  6. let f0 = x as $into;
  7. let f1: $into = $fn(x);
  8. // This makes sure that the conversion produced the best rounding possible, and does
  9. // this independent of `x as $into` rounding correctly.
  10. // This assumes that float to integer conversion is correct.
  11. let y_minus_ulp = <$into>::from_bits(f1.to_bits().wrapping_sub(1)) as $from;
  12. let y = f1 as $from;
  13. let y_plus_ulp = <$into>::from_bits(f1.to_bits().wrapping_add(1)) as $from;
  14. let error_minus = <$from as Int>::abs_diff(y_minus_ulp, x);
  15. let error = <$from as Int>::abs_diff(y, x);
  16. let error_plus = <$from as Int>::abs_diff(y_plus_ulp, x);
  17. // The first two conditions check that none of the two closest float values are
  18. // strictly closer in representation to `x`. The second makes sure that rounding is
  19. // towards even significand if two float values are equally close to the integer.
  20. if error_minus < error
  21. || error_plus < error
  22. || ((error_minus == error || error_plus == error)
  23. && ((f0.to_bits() & 1) != 0))
  24. {
  25. panic!(
  26. "incorrect rounding by {}({}): {}, ({}, {}, {}), errors ({}, {}, {})",
  27. stringify!($fn),
  28. x,
  29. f1.to_bits(),
  30. y_minus_ulp,
  31. y,
  32. y_plus_ulp,
  33. error_minus,
  34. error,
  35. error_plus,
  36. );
  37. }
  38. // Test against native conversion. We disable testing on all `x86` because of
  39. // rounding bugs with `i686`. `powerpc` also has the same rounding bug.
  40. if f0 != f1 && !cfg!(any(
  41. target_arch = "x86",
  42. target_arch = "powerpc",
  43. target_arch = "powerpc64"
  44. )) {
  45. panic!(
  46. "{}({}): std: {}, builtins: {}",
  47. stringify!($fn),
  48. x,
  49. f0,
  50. f1,
  51. );
  52. }
  53. });
  54. )*
  55. };
  56. }
  57. #[test]
  58. fn int_to_float() {
  59. use compiler_builtins::float::conv::{
  60. __floatdidf, __floatdisf, __floatsidf, __floatsisf, __floattidf, __floattisf,
  61. __floatundidf, __floatundisf, __floatunsidf, __floatunsisf, __floatuntidf, __floatuntisf,
  62. };
  63. use compiler_builtins::int::Int;
  64. i_to_f!(
  65. u32, f32, __floatunsisf;
  66. u32, f64, __floatunsidf;
  67. i32, f32, __floatsisf;
  68. i32, f64, __floatsidf;
  69. u64, f32, __floatundisf;
  70. u64, f64, __floatundidf;
  71. i64, f32, __floatdisf;
  72. i64, f64, __floatdidf;
  73. u128, f32, __floatuntisf;
  74. u128, f64, __floatuntidf;
  75. i128, f32, __floattisf;
  76. i128, f64, __floattidf;
  77. );
  78. }
  79. macro_rules! f_to_i {
  80. ($x:ident, $($f:ty, $fn:ident);*;) => {
  81. $(
  82. // it is undefined behavior in the first place to do conversions with NaNs
  83. if !$x.is_nan() {
  84. let conv0 = $x as $f;
  85. let conv1: $f = $fn($x);
  86. if conv0 != conv1 {
  87. panic!("{}({}): std: {}, builtins: {}", stringify!($fn), $x, conv0, conv1);
  88. }
  89. }
  90. )*
  91. };
  92. }
  93. #[test]
  94. fn float_to_int() {
  95. use compiler_builtins::float::conv::{
  96. __fixdfdi, __fixdfsi, __fixdfti, __fixsfdi, __fixsfsi, __fixsfti, __fixunsdfdi,
  97. __fixunsdfsi, __fixunsdfti, __fixunssfdi, __fixunssfsi, __fixunssfti,
  98. };
  99. fuzz_float(N, |x: f32| {
  100. f_to_i!(x,
  101. u32, __fixunssfsi;
  102. u64, __fixunssfdi;
  103. u128, __fixunssfti;
  104. i32, __fixsfsi;
  105. i64, __fixsfdi;
  106. i128, __fixsfti;
  107. );
  108. });
  109. fuzz_float(N, |x: f64| {
  110. f_to_i!(x,
  111. u32, __fixunsdfsi;
  112. u64, __fixunsdfdi;
  113. u128, __fixunsdfti;
  114. i32, __fixdfsi;
  115. i64, __fixdfdi;
  116. i128, __fixdfti;
  117. );
  118. });
  119. }