如何实现给定单字节浮点数解包函数的逆向打包功能?
unpack Function: Implementing pack First, let's break down how your original unpack function maps a byte to a double, then reverse each step to build the inverse function.
Step 1: Break Down the Original unpack Logic
Let's restate the core behavior clearly to make reversal easier:
Given a byte count, the function first computes xor = count ^ 0xFF, then branches into three cases:
- If
xor == 0x08: returns0.0 - If the lower 3 bits of
xor(calledexponent) are0: returnsxor / 512.0 - Otherwise: combines the upper 5 bits of
xor(calledmantissa) with a leading1bit, shifts left byexponent-1, then divides by 512 to get the final double.
Since count = xor ^ 0xFF, our goal is to find the correct xor value for a given double, then compute count from it.
Step 2: Define the Inverse pack Logic
We'll mirror the three cases from unpack to cover all valid inputs:
Case 1: Input is 0.0
From unpack, we know 0.0 maps directly to xor = 0x08. So:count = 0x08 ^ 0xFF = 0xF7
Case 2: Input fits the "exponent 0" range
In unpack, this case returns xor / 512.0 where:
xoris an integer between 0-255- The lower 3 bits of
xorare0(soxoris a multiple of 8) xor != 0x08(already handled in Case 1)
For a given double v, calculate xor_candidate = v * 512. To be valid:
xor_candidatemust be a whole number within 0-255xor_candidate % 8 == 0xor_candidate != 0x08
If all checks pass, count = xor_candidate ^ 0xFF
Case 3: Input fits the "non-zero exponent" range
The original formula here is:v = ((mantissa | 0x100) << (exponent - 1)) / 512.0
Where:
mantissa = xor & 0xF8(upper 5 bits ofxor, multiples of 8)exponent = xor & 0x07(values 1-7, since exponent 0 is Case 2)
Rewriting to solve for components:
Multiply both sides by 1024 (2^10) to simplify:v * 1024 = (0x100 | mantissa) * (2 ^ exponent)
0x100 | mantissa is a 9-bit number ranging from 0x100 (256) to 0x1F8 (504), in steps of 8. For a given v, loop through exponents 1-7 to find one where:(v * 1024) / (2^exponent) is an integer between 256-504, and divisible by 8.
Once found:
mantissa = combined_value - 0x100xor = mantissa | exponentcount = xor ^ 0xFF
Step 3: Implement the pack Function
Here's the C# implementation matching your original code's language:
private byte pack(double value) { // Case 1: Handle 0.0 directly if (Math.Abs(value) < 1e-9) // Account for floating point precision { return (byte)(0x08 ^ 0xFF); // Returns 0xF7 } // Case 2: Check if value fits the exponent 0 range double xorCandidateDouble = value * 512.0; if (xorCandidateDouble >= 0 && xorCandidateDouble <= 255) { int roundedCandidate = (int)Math.Round(xorCandidateDouble); // Check if the value is nearly an integer, meets divisibility, and isn't 0x08 if (Math.Abs(xorCandidateDouble - roundedCandidate) < 1e-9 && (roundedCandidate & 0x07) == 0 && roundedCandidate != 0x08) { return (byte)(roundedCandidate ^ 0xFF); } } // Case 3: Find valid exponent and mantissa for non-zero exponent range for (int exponent = 1; exponent <= 7; exponent++) { double combinedDouble = value * 1024.0 / Math.Pow(2, exponent); if (combinedDouble >= 256 && combinedDouble <= 504) { int combined = (int)Math.Round(combinedDouble); if (Math.Abs(combinedDouble - combined) < 1e-9 && combined % 8 == 0) { byte mantissa = (byte)(combined - 0x100); byte xor = (byte)(mantissa | exponent); return (byte)(xor ^ 0xFF); } } } // Throw error if no valid mapping exists (adjust to return default if needed) throw new ArgumentOutOfRangeException(nameof(value), "Input double has no valid corresponding byte from the original unpack function."); }
Key Notes
- Floating Point Precision: We use a small epsilon (
1e-9) to compare values, since doubles can't represent all decimal values exactly. - Error Handling: The function throws an exception for inputs that don't map to any valid byte from
unpack—you can adjust this to return a default byte if your use case requires it. - Validation: Each case includes checks to ensure the input fits the valid ranges of the original
unpackfunction.
内容的提问来源于stack exchange,提问作者user1930728

