 # Strange floating-point results with transcendental functions

All the functions and their inverses below work, with the exception of: sine and cosine. The hyperbolic versions of each work, but the base functions do not. The assertions fail when: the random value which determines which function to use happens to choose sine or cosine, then the inverse does not map correctly to the original input:

``````/// applies a pseudo-random function to the input. This is used to produce a secure value which identifies a packet.
///
/// It is close to necessary for the drill to be used in order to find the correct values, since `op_value` comes from
/// the drill. The produced floating point value must be correct forall digits
///
/// Produces a floating-point number. All functions must necessarily be surjective
#[inline]
fn get_op_result_by_input(op_value: u8, coefficient: u32, input: u32) -> f64 {
println!("op_value: {}", op_value);
println!("{} * op({})", coefficient, input);
let input = input as f64;
match op_value {
0..32 => {
coefficient as f64 * (input).ln()
},

32..64 => {
coefficient as f64 * (input).sin()
},

64..96 => {
coefficient as f64 * (input).cos()
},

96..128 => {
coefficient as f64 * (input).tan()
},

128..160 => {
coefficient as f64 * (input).sinh()
},

160..192 => {
coefficient as f64 * (input).cosh()
},

192..224 => {
coefficient as f64 * (input).tanh()
},

224..=255 => {
coefficient as f64 * (input).recip()
}
}
}

#[inline]
fn get_op_result_by_input_inv(op_value: u8, coefficient: u32, input: f64) -> usize {
println!("op_value: {}", op_value);
let ka = input / f64::from(coefficient);
println!("{} / {} = ka == {}", coefficient, input, ka);
match op_value {
// natural log
0..32 => {
ka.exp().round() as usize
},

32..64 => {
ka.asin().round() as usize
},

64..96 => {
ka.acos().round() as usize
},

96..128 => {
ka.atan().round() as usize
},

128..160 => {
ka.asinh().round() as usize
},

160..192 => {
ka.acosh().round() as usize
},

192..224 => {
ka.atanh().round() as usize
},

224..=255 => {
ka.recip().round() as usize
}
}
}

/// `x` should be the packet number in the wave
pub fn calculate_pid<Drx: DrillType>(x: usize, drill: &Drx) -> f64 {
get_op_result_by_input(drill.get_low()[VIRTUAL_TIME_INDEX], drill.get_high()[VIRTUAL_TIME_INDEX], x as u32)
}

#[inline]
/// The inverse function of `calculate_pid`
pub fn calculate_pid_inv<Drx: DrillType>(y: f64, drill: &Drx) -> usize {
get_op_result_by_input_inv(drill.get_low()[VIRTUAL_TIME_INDEX], drill.get_high()[VIRTUAL_TIME_INDEX], y)
}

/// `x` should be the wave number in the wave series
pub fn calculate_wid<Drx: DrillType>(x: usize, drill: &Drx) -> f64 {
get_op_result_by_input(drill.get_low()[VIRTUAL_TIME_INDEX], drill.get_high()[VIRTUAL_TIME_INDEX], x as u32)
}

#[inline]
/// The inverse function of `calculate_wid`
pub fn calculate_wid_inv<Drx: DrillType>(y: f64, drill: &Drx) -> usize {
get_op_result_by_input_inv(drill.get_low()[VIRTUAL_TIME_INDEX], drill.get_high()[VIRTUAL_TIME_INDEX], y)
}
``````

And (part) of the unit test:

``````            let input = 10;
let pid = calculate_pid(input, drill.deref());
println!("-> {}", pid);
let pid_inv = calculate_pid_inv(pid, drill.deref());
println!("-> {}", pid_inv);
assert_eq!(input, pid_inv);
Ok(())
``````

What's an example of an input value that fails your check? Sine and cosine are periodic functions, and arcsin(sin(x)) == x will only hold in part of the domain

2 Likes

Oh, that mathematical discrepancy may be why then. So, sine and sine inverse aren't necessarily surjective? I thought the sine inverse of sin(x) returns x for all x in sine's domain.

You can restrict your definition of sine to sin x where x in -π/2 <= x <= π/2 and then you have a function which has an actual inverse - arcsin - because this sin x has a one-to-one correspondance between input and output points (a bijection).

3 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.