Skip to content

Commit ad47404

Browse files
implemented scale_b
1 parent b976768 commit ad47404

File tree

4 files changed

+5688
-19
lines changed

4 files changed

+5688
-19
lines changed

make_scale_b_test_cases.sh

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#!/bin/bash
2+
# SPDX-License-Identifier: LGPL-2.1-or-later
3+
# See Notices.txt for copyright information
4+
set -e
5+
6+
if [[ -z "$SOFTFLOAT_VERIFY" ]] && ! SOFTFLOAT_VERIFY="`which softfloat-verify`"; then
7+
echo "can't find softfloat-verify in PATH" >&2
8+
echo "get it from https://salsa.debian.org/Kazan-team/softfloat-verify" >&2
9+
echo "then put built executable in PATH or set" >&2
10+
echo "SOFTFLOAT_VERIFY to path of executable" >&2
11+
exit 1
12+
fi
13+
14+
function fail() {
15+
echo "$*">&2
16+
exit 1
17+
}
18+
19+
function write_test_case() {
20+
local value1="$1"
21+
local scale="$2"
22+
local rounding_mode="$3"
23+
local tininess_detection_mode="$4"
24+
printf -v value1 "0x%04X" $((value1))
25+
printf -v scale "%6i" $((scale))
26+
local float_scale
27+
printf -v float_scale "0x%04X%028i" $((scale + 0x3FFF)) 0
28+
local sf_rounding_mode
29+
case "$rounding_mode" in
30+
TiesToEven)
31+
sf_rounding_mode=near_even
32+
;;
33+
TowardZero)
34+
sf_rounding_mode=minMag
35+
;;
36+
TowardNegative)
37+
sf_rounding_mode=min
38+
;;
39+
TowardPositive)
40+
sf_rounding_mode=max
41+
;;
42+
TiesToAway)
43+
sf_rounding_mode=near_maxMag
44+
;;
45+
*)
46+
fail "invalid rounding mode: $rounding_mode"
47+
;;
48+
esac
49+
local input="softfloat_round_$sf_rounding_mode softfloat_roundingMode_write_helper"
50+
input+=" 0 softfloat_exceptionFlags_write_helper"
51+
input+=" softfloat_tininess_${tininess_detection_mode,} softfloat_detectTininess_write_helper"
52+
input+=" $value1"
53+
input+=" f16_to_f128 $float_scale f128_mul f128_to_f16"
54+
input+=" softfloat_exceptionFlags_read_helper"
55+
input+=" softfloat_flag_inexact"
56+
input+=" softfloat_flag_underflow"
57+
input+=" softfloat_flag_overflow"
58+
input+=" softfloat_flag_infinite"
59+
input+=" softfloat_flag_invalid"
60+
local output
61+
output=(`echo "$input" | "$SOFTFLOAT_VERIFY"`) || fail $'softfloat-verify failed. input:\n'"$input"
62+
((${#output[@]} == 7)) || fail $'softfloat-verify returned invalid number of outputs. input:\n'"$input"
63+
local result="${output[0]}"
64+
local flags="${output[1]}"
65+
local flag_inexact="${output[2]}"
66+
local flag_underflow="${output[3]}"
67+
local flag_overflow="${output[4]}"
68+
local flag_infinite="${output[5]}"
69+
local flag_invalid="${output[6]}"
70+
local decoded_flags=()
71+
((flags & flag_inexact)) && decoded_flags+=("INEXACT")
72+
((flags & flag_underflow)) && decoded_flags+=("UNDERFLOW")
73+
((flags & flag_overflow)) && decoded_flags+=("OVERFLOW")
74+
((flags & flag_infinite)) && decoded_flags+=("DIVISION_BY_ZERO")
75+
((flags & flag_invalid)) && decoded_flags+=("INVALID_OPERATION")
76+
if (( ${#decoded_flags[@]} )); then
77+
printf -v flags "%s|" "${decoded_flags[@]}"
78+
flags="${flags%%|}"
79+
else
80+
flags="(empty)"
81+
fi
82+
if (((result & 0x7C00) == 0x7C00 && (result & 0x3FF) != 0)); then
83+
result=0x7E00
84+
fi
85+
printf -v result "0x%04X" $((result))
86+
echo "$value1 $scale $rounding_mode $tininess_detection_mode $result $flags"
87+
}
88+
89+
test_case_list=(0x0000 0x0001 0x03FF 0x0400 0x3C00 0x3C01 0x7BFF 0x7C00 0x7C01 0x7DFF 0x7E00 0x7FFF)
90+
test_case_list+=(0x8000 0x8001 0x83FF 0x8400 0xBC00 0xBC01 0xFBFF 0xFC00 0xFC01 0xFDFF 0xFE00 0xFFFF)
91+
scale_list=(-10000 -41 -40 -39 -17 -16 -15 -14 -2 -1 0)
92+
scale_list+=(10000 41 40 39 17 16 15 14 2 1)
93+
rounding_modes=(TiesToEven TowardZero TowardNegative TowardPositive TiesToAway)
94+
tininess_detection_modes=(BeforeRounding AfterRounding)
95+
96+
exec > "test_data/scale_b.txt"
97+
first=1
98+
for rounding_mode in "${rounding_modes[@]}"; do
99+
for tininess_detection_mode in "${tininess_detection_modes[@]}"; do
100+
for scale in "${scale_list[@]}"; do
101+
if ((first)); then
102+
first=0
103+
else
104+
echo
105+
fi
106+
echo "# testing F16::scale_b(X, $scale) with $rounding_mode $tininess_detection_mode"
107+
for value1 in "${test_case_list[@]}"; do
108+
write_test_case $value1 $scale $rounding_mode $tininess_detection_mode
109+
done
110+
printf "." >&2
111+
done
112+
done
113+
done
114+
echo >&2

src/lib.rs

+73-6
Original file line numberDiff line numberDiff line change
@@ -746,6 +746,7 @@ pub struct PlatformProperties {
746746
pub fma_inf_zero_qnan_result: FMAInfZeroQNaNResult,
747747
pub round_to_integral_nan_propagation_mode: UnaryNaNPropagationMode,
748748
pub next_up_or_down_nan_propagation_mode: UnaryNaNPropagationMode,
749+
pub scale_b_nan_propagation_mode: UnaryNaNPropagationMode,
749750
}
750751

751752
impl Default for PlatformProperties {
@@ -800,6 +801,7 @@ macro_rules! platform_properties_constants {
800801
fma_inf_zero_qnan_result,
801802
round_to_integral_nan_propagation_mode,
802803
next_up_or_down_nan_propagation_mode,
804+
scale_b_nan_propagation_mode,
803805
#[fn] quiet_nan_format,
804806
)
805807
}
@@ -820,6 +822,7 @@ platform_properties_constants! {
820822
fma_inf_zero_qnan_result: FMAInfZeroQNaNResult::CanonicalAndGenerateInvalid,
821823
round_to_integral_nan_propagation_mode: UnaryNaNPropagationMode::First,
822824
next_up_or_down_nan_propagation_mode: UnaryNaNPropagationMode::First,
825+
scale_b_nan_propagation_mode: UnaryNaNPropagationMode::First,
823826
};
824827
pub const RISC_V: PlatformProperties = PlatformProperties {
825828
canonical_nan_sign: Sign::Positive,
@@ -831,6 +834,7 @@ platform_properties_constants! {
831834
fma_inf_zero_qnan_result: FMAInfZeroQNaNResult::CanonicalAndGenerateInvalid,
832835
round_to_integral_nan_propagation_mode: UnaryNaNPropagationMode::AlwaysCanonical,
833836
next_up_or_down_nan_propagation_mode: UnaryNaNPropagationMode::AlwaysCanonical,
837+
scale_b_nan_propagation_mode: UnaryNaNPropagationMode::AlwaysCanonical,
834838
};
835839
pub const POWER: PlatformProperties = PlatformProperties {
836840
canonical_nan_sign: Sign::Positive,
@@ -843,6 +847,7 @@ platform_properties_constants! {
843847
fma_inf_zero_qnan_result: FMAInfZeroQNaNResult::PropagateAndGenerateInvalid,
844848
round_to_integral_nan_propagation_mode: UnaryNaNPropagationMode::First,
845849
next_up_or_down_nan_propagation_mode: UnaryNaNPropagationMode::First,
850+
scale_b_nan_propagation_mode: UnaryNaNPropagationMode::First,
846851
};
847852
pub const MIPS_2008: PlatformProperties = PlatformProperties {
848853
canonical_nan_sign: Sign::Positive,
@@ -855,6 +860,7 @@ platform_properties_constants! {
855860
fma_inf_zero_qnan_result: FMAInfZeroQNaNResult::PropagateAndGenerateInvalid,
856861
round_to_integral_nan_propagation_mode: UnaryNaNPropagationMode::First,
857862
next_up_or_down_nan_propagation_mode: UnaryNaNPropagationMode::First,
863+
scale_b_nan_propagation_mode: UnaryNaNPropagationMode::First,
858864
};
859865
// X86_X87 is not implemented
860866
pub const X86_SSE: PlatformProperties = PlatformProperties {
@@ -868,6 +874,7 @@ platform_properties_constants! {
868874
fma_inf_zero_qnan_result: FMAInfZeroQNaNResult::FollowNaNPropagationMode,
869875
round_to_integral_nan_propagation_mode: UnaryNaNPropagationMode::First,
870876
next_up_or_down_nan_propagation_mode: UnaryNaNPropagationMode::First,
877+
scale_b_nan_propagation_mode: UnaryNaNPropagationMode::First,
871878
};
872879
pub const SPARC: PlatformProperties = PlatformProperties {
873880
canonical_nan_sign: Sign::Positive,
@@ -880,6 +887,7 @@ platform_properties_constants! {
880887
fma_inf_zero_qnan_result: FMAInfZeroQNaNResult::FollowNaNPropagationMode,
881888
round_to_integral_nan_propagation_mode: UnaryNaNPropagationMode::First,
882889
next_up_or_down_nan_propagation_mode: UnaryNaNPropagationMode::First,
890+
scale_b_nan_propagation_mode: UnaryNaNPropagationMode::First,
883891
};
884892
pub const HPPA: PlatformProperties = PlatformProperties {
885893
canonical_nan_sign: Sign::Positive,
@@ -892,6 +900,7 @@ platform_properties_constants! {
892900
fma_inf_zero_qnan_result: FMAInfZeroQNaNResult::FollowNaNPropagationMode,
893901
round_to_integral_nan_propagation_mode: UnaryNaNPropagationMode::First,
894902
next_up_or_down_nan_propagation_mode: UnaryNaNPropagationMode::First,
903+
scale_b_nan_propagation_mode: UnaryNaNPropagationMode::First,
895904
};
896905
pub const MIPS_LEGACY: PlatformProperties = PlatformProperties {
897906
canonical_nan_sign: Sign::Positive,
@@ -904,6 +913,7 @@ platform_properties_constants! {
904913
fma_inf_zero_qnan_result: FMAInfZeroQNaNResult::CanonicalAndGenerateInvalid,
905914
round_to_integral_nan_propagation_mode: UnaryNaNPropagationMode::First,
906915
next_up_or_down_nan_propagation_mode: UnaryNaNPropagationMode::First,
916+
scale_b_nan_propagation_mode: UnaryNaNPropagationMode::First,
907917
};
908918
}
909919

@@ -1827,6 +1837,7 @@ impl<Bits: FloatBitsType, FT: FloatTraits<Bits = Bits>> Float<FT> {
18271837
pub fn into_quiet_nan(mut self) -> Self {
18281838
let properties = self.properties();
18291839
self.set_exponent_field(properties.exponent_inf_nan::<Bits>());
1840+
// FIXME: handle nan propagation properly
18301841
match properties.quiet_nan_format() {
18311842
QuietNaNFormat::Standard => self.set_mantissa_field_msb(true),
18321843
QuietNaNFormat::MIPSLegacy => return Self::quiet_nan_with_traits(self.traits),
@@ -2234,7 +2245,7 @@ impl<Bits: FloatBitsType, FT: FloatTraits<Bits = Bits>> Float<FT> {
22342245
Self::quiet_nan_with_traits(self.traits.clone())
22352246
} else if rhs_class.is_infinity() {
22362247
if self_class.is_zero() {
2237-
self.clone()
2248+
Self::signed_zero_with_traits(self.sign(), self.traits.clone())
22382249
} else {
22392250
Self::from_real_algebraic_number_with_traits(
22402251
&self.to_real_algebraic_number().expect("known to be finite"),
@@ -2332,12 +2343,13 @@ impl<Bits: FloatBitsType, FT: FloatTraits<Bits = Bits>> Float<FT> {
23322343
{
23332344
fp_state.status_flags |= StatusFlags::INVALID_OPERATION;
23342345
Self::quiet_nan_with_traits(self.traits.clone())
2335-
} else if ((self_class.is_zero() || factor_class.is_zero())
2346+
} else if (self_class.is_zero() || factor_class.is_zero())
23362347
&& term_class.is_zero()
2337-
&& product_sign == term.sign())
2338-
|| term_class.is_infinity()
2348+
&& product_sign == term.sign()
23392349
{
2340-
term.clone()
2350+
Self::signed_zero_with_traits(product_sign, self.traits.clone())
2351+
} else if term_class.is_infinity() {
2352+
Self::signed_infinity_with_traits(term.sign(), self.traits.clone())
23412353
} else if self_class.is_infinity() || factor_class.is_infinity() {
23422354
Self::signed_infinity_with_traits(product_sign, self.traits.clone())
23432355
} else {
@@ -2458,7 +2470,7 @@ impl<Bits: FloatBitsType, FT: FloatTraits<Bits = Bits>> Float<FT> {
24582470
UnaryNaNPropagationResults::First => self.to_quiet_nan(),
24592471
}
24602472
} else if class.is_infinity() {
2461-
self.clone()
2473+
Self::signed_infinity_with_traits(self.sign(), self.traits.clone())
24622474
} else {
24632475
let value = self
24642476
.round_to_bigint(exact, Some(rounding_mode), Some(fp_state))
@@ -2609,6 +2621,60 @@ impl<Bits: FloatBitsType, FT: FloatTraits<Bits = Bits>> Float<FT> {
26092621
}
26102622
Some(exponent)
26112623
}
2624+
pub fn scale_b(
2625+
&self,
2626+
mut scale: BigInt,
2627+
rounding_mode: Option<RoundingMode>,
2628+
fp_state: Option<&mut FPState>,
2629+
) -> Self {
2630+
let mut default_fp_state = FPState::default();
2631+
let fp_state = fp_state.unwrap_or(&mut default_fp_state);
2632+
let rounding_mode = rounding_mode.unwrap_or(fp_state.rounding_mode);
2633+
let properties = self.properties();
2634+
let class = self.class();
2635+
if class.is_nan() {
2636+
if class.is_signaling_nan() {
2637+
fp_state.status_flags |= StatusFlags::INVALID_OPERATION;
2638+
}
2639+
match properties
2640+
.platform_properties()
2641+
.scale_b_nan_propagation_mode
2642+
.calculate_propagation_results(class)
2643+
{
2644+
UnaryNaNPropagationResults::Canonical => {
2645+
Self::quiet_nan_with_traits(self.traits.clone())
2646+
}
2647+
UnaryNaNPropagationResults::First => self.to_quiet_nan(),
2648+
}
2649+
} else if class.is_infinity() {
2650+
Self::signed_infinity_with_traits(self.sign(), self.traits.clone())
2651+
} else if class.is_zero() {
2652+
Self::signed_zero_with_traits(self.sign(), self.traits.clone())
2653+
} else {
2654+
let exponent_max_normal: BigInt = properties.exponent_max_normal::<Bits>().into();
2655+
let exponent_min_normal: BigInt = properties.exponent_min_normal::<Bits>().into();
2656+
let scale_limit: BigInt =
2657+
(exponent_max_normal - exponent_min_normal + properties.fraction_width() + 1) * 2;
2658+
scale = scale.max(-&scale_limit);
2659+
scale = scale.min(scale_limit);
2660+
let mut value = self.to_real_algebraic_number().expect("known to be finite");
2661+
if scale.is_positive() {
2662+
value *= RealAlgebraicNumber::from(
2663+
BigInt::one() << scale.to_usize().expect("rhs won't fit in usize"),
2664+
);
2665+
} else {
2666+
value /= RealAlgebraicNumber::from(
2667+
BigInt::one() << (-scale).to_usize().expect("-rhs won't fit in usize"),
2668+
);
2669+
}
2670+
Self::from_real_algebraic_number_with_traits(
2671+
&value,
2672+
Some(rounding_mode),
2673+
Some(fp_state),
2674+
self.traits.clone(),
2675+
)
2676+
}
2677+
}
26122678
}
26132679

26142680
impl<Bits: FloatBitsType, FT: FloatTraits<Bits = Bits>> fmt::Debug for Float<FT> {
@@ -2775,6 +2841,7 @@ mod tests {
27752841
fma_inf_zero_qnan_result: CanonicalAndGenerateInvalid, \
27762842
round_to_integral_nan_propagation_mode: First, \
27772843
next_up_or_down_nan_propagation_mode: First, \
2844+
scale_b_nan_propagation_mode: First, \
27782845
quiet_nan_format: MIPSLegacy }), \
27792846
bits: 0x1234, sign: Positive, exponent_field: 0x04, \
27802847
mantissa_field: 0x234, class: PositiveNormal }",

src/test_cases.rs

+42-13
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn test_case_argument_same<T: TestCaseArgument + Sized, SameFn: FnOnce(&T, &T) -
2323
}
2424

2525
macro_rules! impl_test_case_argument_for_int {
26-
($t:ident) => {
26+
($t:ident, $unsigned_t:ident) => {
2727
impl TestCaseArgument for $t {
2828
fn parse_into(&mut self, text: &str) -> Result<(), String> {
2929
let mut bytes = text.bytes();
@@ -83,8 +83,16 @@ macro_rules! impl_test_case_argument_for_int {
8383
fn same(&self, other: &dyn TestCaseArgument) -> bool {
8484
test_case_argument_same(self, other, PartialEq::eq)
8585
}
86+
#[allow(unused_comparisons)]
8687
fn debug(&self) -> String {
87-
format!("{:#X}", self)
88+
if *self < 0 {
89+
format!(
90+
"-{:#X}",
91+
($unsigned_t::max_value() - *self as $unsigned_t) + 1
92+
)
93+
} else {
94+
format!("{:#X}", self)
95+
}
8896
}
8997
fn as_any(&self) -> &dyn Any {
9098
self
@@ -93,16 +101,16 @@ macro_rules! impl_test_case_argument_for_int {
93101
};
94102
}
95103

96-
impl_test_case_argument_for_int!(u8);
97-
impl_test_case_argument_for_int!(u16);
98-
impl_test_case_argument_for_int!(u32);
99-
impl_test_case_argument_for_int!(u64);
100-
impl_test_case_argument_for_int!(u128);
101-
impl_test_case_argument_for_int!(i8);
102-
impl_test_case_argument_for_int!(i16);
103-
impl_test_case_argument_for_int!(i32);
104-
impl_test_case_argument_for_int!(i64);
105-
impl_test_case_argument_for_int!(i128);
104+
impl_test_case_argument_for_int!(u8, u8);
105+
impl_test_case_argument_for_int!(u16, u16);
106+
impl_test_case_argument_for_int!(u32, u32);
107+
impl_test_case_argument_for_int!(u64, u64);
108+
impl_test_case_argument_for_int!(u128, u128);
109+
impl_test_case_argument_for_int!(i8, u8);
110+
impl_test_case_argument_for_int!(i16, u16);
111+
impl_test_case_argument_for_int!(i32, u32);
112+
impl_test_case_argument_for_int!(i64, u64);
113+
impl_test_case_argument_for_int!(i128, u128);
106114

107115
impl TestCaseArgument for F16 {
108116
fn parse_into(&mut self, text: &str) -> Result<(), String> {
@@ -232,7 +240,7 @@ trait TestCase {
232240
fn io(&mut self) -> TestCaseIO<'_>;
233241
fn calculate(&mut self, location: FileLocation);
234242
fn parse_and_run(&mut self, test_case: &str, location: FileLocation) {
235-
let mut arguments_text = test_case.split(' ');
243+
let mut arguments_text = test_case.split(' ').filter(|v| !v.is_empty());
236244
let io = self.io();
237245
for argument in io.inputs {
238246
if let Some(argument_text) = arguments_text.next() {
@@ -696,3 +704,24 @@ test_case! {
696704
*down_status_flags = fp_state.status_flags;
697705
}
698706
}
707+
708+
test_case! {
709+
#[test_case_file_name = "scale_b.txt"]
710+
fn test_scale_b(value: F16,
711+
scale: i64,
712+
rounding_mode: RoundingMode,
713+
tininess_detection_mode: TininessDetectionMode,
714+
#[output] result: F16,
715+
#[output] status_flags: StatusFlags,
716+
) {
717+
let exception_handling_mode = ExceptionHandlingMode::DefaultIgnoreExactUnderflow;
718+
let mut fp_state = FPState {
719+
rounding_mode,
720+
exception_handling_mode,
721+
tininess_detection_mode,
722+
..FPState::default()
723+
};
724+
*result = value.scale_b(scale.into(), None, Some(&mut fp_state));
725+
*status_flags = fp_state.status_flags;
726+
}
727+
}

0 commit comments

Comments
 (0)