Skip to content

Commit a6f9ffd

Browse files
feat: add comprehensive temperature conversion function (#1002)
1 parent 8385617 commit a6f9ffd

File tree

3 files changed

+257
-0
lines changed

3 files changed

+257
-0
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
* [Roman Numerals](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/roman_numerals.rs)
9090
* [Speed](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/speed.rs)
9191
* [Time Units](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/time_units.rs)
92+
* [Temperature](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/temperature.rs)
9293
* Data Structures
9394
* [AVL Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/avl_tree.rs)
9495
* [B-Tree](https://github.com/TheAlgorithms/Rust/blob/master/src/data_structures/b_tree.rs)

src/conversions/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ mod rgb_cmyk_conversion;
1818
mod rgb_hsv_conversion;
1919
mod roman_numerals;
2020
mod speed;
21+
mod temperature;
2122
mod time_units;
2223

2324
pub use self::binary_to_decimal::binary_to_decimal;
@@ -42,4 +43,5 @@ pub use self::rgb_cmyk_conversion::rgb_to_cmyk;
4243
pub use self::rgb_hsv_conversion::{hsv_to_rgb, rgb_to_hsv, ColorError, Hsv, Rgb};
4344
pub use self::roman_numerals::{int_to_roman, roman_to_int};
4445
pub use self::speed::{convert_speed, SpeedUnit};
46+
pub use self::temperature::{convert_temperature, TemperatureUnit};
4547
pub use self::time_units::convert_time;

src/conversions/temperature.rs

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
//! Convert between different units of temperature
2+
//!
3+
//! Supports conversions between 8 temperature scales using Kelvin as an intermediary:
4+
//! - Kelvin (K) - SI base unit, absolute scale
5+
//! - Celsius (°C) - Standard metric scale
6+
//! - Fahrenheit (°F) - Imperial scale
7+
//! - Rankine (°R) - Absolute Fahrenheit scale
8+
//! - Delisle (°De) - Historical inverted scale (higher values = colder)
9+
//! - Newton (°N) - Historical scale by Isaac Newton
10+
//! - Réaumur (°Ré) - Historical European scale
11+
//! - Rømer (°Rø) - Historical Danish scale
12+
13+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14+
pub enum TemperatureUnit {
15+
Kelvin,
16+
Celsius,
17+
Fahrenheit,
18+
Rankine,
19+
Delisle,
20+
Newton,
21+
Reaumur,
22+
Romer,
23+
}
24+
25+
impl TemperatureUnit {
26+
fn to_kelvin(self, value: f64) -> f64 {
27+
match self {
28+
TemperatureUnit::Kelvin => value,
29+
TemperatureUnit::Celsius => value + 273.15,
30+
TemperatureUnit::Fahrenheit => (value + 459.67) * 5.0 / 9.0,
31+
TemperatureUnit::Rankine => value * 5.0 / 9.0,
32+
TemperatureUnit::Delisle => 373.15 - value * 2.0 / 3.0,
33+
TemperatureUnit::Newton => value * 100.0 / 33.0 + 273.15,
34+
TemperatureUnit::Reaumur => value * 5.0 / 4.0 + 273.15,
35+
TemperatureUnit::Romer => (value - 7.5) * 40.0 / 21.0 + 273.15,
36+
}
37+
}
38+
39+
fn kelvin_to_unit(self, kelvin: f64) -> f64 {
40+
match self {
41+
TemperatureUnit::Kelvin => kelvin,
42+
TemperatureUnit::Celsius => kelvin - 273.15,
43+
TemperatureUnit::Fahrenheit => kelvin * 9.0 / 5.0 - 459.67,
44+
TemperatureUnit::Rankine => kelvin * 9.0 / 5.0,
45+
TemperatureUnit::Delisle => (373.15 - kelvin) * 3.0 / 2.0,
46+
TemperatureUnit::Newton => (kelvin - 273.15) * 33.0 / 100.0,
47+
TemperatureUnit::Reaumur => (kelvin - 273.15) * 4.0 / 5.0,
48+
TemperatureUnit::Romer => (kelvin - 273.15) * 21.0 / 40.0 + 7.5,
49+
}
50+
}
51+
}
52+
53+
pub fn convert_temperature(value: f64, from: TemperatureUnit, to: TemperatureUnit) -> f64 {
54+
let kelvin = from.to_kelvin(value);
55+
to.kelvin_to_unit(kelvin)
56+
}
57+
58+
#[cfg(test)]
59+
mod tests {
60+
use super::*;
61+
62+
const EPSILON: f64 = 1e-10;
63+
64+
fn approx_eq(a: f64, b: f64) -> bool {
65+
(a - b).abs() < EPSILON
66+
}
67+
68+
#[test]
69+
fn test_celsius_conversions() {
70+
assert!(approx_eq(
71+
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit),
72+
32.0
73+
));
74+
assert!(approx_eq(
75+
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit),
76+
212.0
77+
));
78+
assert!(approx_eq(
79+
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Kelvin),
80+
273.15
81+
));
82+
assert!(approx_eq(
83+
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Rankine),
84+
491.67
85+
));
86+
assert!(approx_eq(
87+
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Delisle),
88+
150.0
89+
));
90+
assert!(approx_eq(
91+
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Newton),
92+
0.0
93+
));
94+
assert!(approx_eq(
95+
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Reaumur),
96+
0.0
97+
));
98+
assert!(approx_eq(
99+
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Romer),
100+
7.5
101+
));
102+
}
103+
104+
#[test]
105+
fn test_fahrenheit_conversions() {
106+
assert!(approx_eq(
107+
convert_temperature(32.0, TemperatureUnit::Fahrenheit, TemperatureUnit::Celsius),
108+
0.0
109+
));
110+
assert!(approx_eq(
111+
convert_temperature(212.0, TemperatureUnit::Fahrenheit, TemperatureUnit::Celsius),
112+
100.0
113+
));
114+
assert!(approx_eq(
115+
convert_temperature(32.0, TemperatureUnit::Fahrenheit, TemperatureUnit::Kelvin),
116+
273.15
117+
));
118+
assert!(approx_eq(
119+
convert_temperature(32.0, TemperatureUnit::Fahrenheit, TemperatureUnit::Rankine),
120+
491.67
121+
));
122+
}
123+
124+
#[test]
125+
fn test_kelvin_conversions() {
126+
assert!(approx_eq(
127+
convert_temperature(273.15, TemperatureUnit::Kelvin, TemperatureUnit::Celsius),
128+
0.0
129+
));
130+
assert!(approx_eq(
131+
convert_temperature(273.15, TemperatureUnit::Kelvin, TemperatureUnit::Fahrenheit),
132+
32.0
133+
));
134+
assert!(approx_eq(
135+
convert_temperature(273.15, TemperatureUnit::Kelvin, TemperatureUnit::Rankine),
136+
491.67
137+
));
138+
}
139+
140+
#[test]
141+
fn test_round_trip_conversions() {
142+
let temp = 25.0;
143+
let units = [
144+
TemperatureUnit::Celsius,
145+
TemperatureUnit::Fahrenheit,
146+
TemperatureUnit::Kelvin,
147+
TemperatureUnit::Rankine,
148+
TemperatureUnit::Delisle,
149+
TemperatureUnit::Newton,
150+
TemperatureUnit::Reaumur,
151+
TemperatureUnit::Romer,
152+
];
153+
154+
for from_unit in units.iter() {
155+
for to_unit in units.iter() {
156+
let converted = convert_temperature(temp, *from_unit, *to_unit);
157+
let back = convert_temperature(converted, *to_unit, *from_unit);
158+
assert!(
159+
approx_eq(back, temp),
160+
"Round trip failed: {from_unit:?} -> {to_unit:?} -> {from_unit:?}: {back} != {temp}"
161+
);
162+
}
163+
}
164+
}
165+
166+
#[test]
167+
fn test_special_temperatures() {
168+
// Absolute zero
169+
assert!(approx_eq(
170+
convert_temperature(0.0, TemperatureUnit::Kelvin, TemperatureUnit::Celsius),
171+
-273.15
172+
));
173+
assert!(approx_eq(
174+
convert_temperature(0.0, TemperatureUnit::Kelvin, TemperatureUnit::Fahrenheit),
175+
-459.67
176+
));
177+
178+
// Water freezing point
179+
assert!(approx_eq(
180+
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit),
181+
32.0
182+
));
183+
assert!(approx_eq(
184+
convert_temperature(0.0, TemperatureUnit::Celsius, TemperatureUnit::Kelvin),
185+
273.15
186+
));
187+
188+
// Water boiling point
189+
assert!(approx_eq(
190+
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit),
191+
212.0
192+
));
193+
assert!(approx_eq(
194+
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Kelvin),
195+
373.15
196+
));
197+
198+
// Celsius equals Fahrenheit
199+
assert!(approx_eq(
200+
convert_temperature(-40.0, TemperatureUnit::Celsius, TemperatureUnit::Fahrenheit),
201+
-40.0
202+
));
203+
}
204+
205+
#[test]
206+
fn test_historical_scales() {
207+
// Delisle (inverted scale)
208+
assert!(approx_eq(
209+
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Delisle),
210+
0.0
211+
));
212+
assert!(approx_eq(
213+
convert_temperature(0.0, TemperatureUnit::Delisle, TemperatureUnit::Celsius),
214+
100.0
215+
));
216+
217+
// Newton scale
218+
assert!(approx_eq(
219+
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Newton),
220+
33.0
221+
));
222+
assert!(approx_eq(
223+
convert_temperature(33.0, TemperatureUnit::Newton, TemperatureUnit::Celsius),
224+
100.0
225+
));
226+
227+
// Rømer scale
228+
assert!(approx_eq(
229+
convert_temperature(100.0, TemperatureUnit::Celsius, TemperatureUnit::Romer),
230+
60.0
231+
));
232+
assert!(approx_eq(
233+
convert_temperature(60.0, TemperatureUnit::Romer, TemperatureUnit::Celsius),
234+
100.0
235+
));
236+
}
237+
238+
#[test]
239+
fn test_same_unit_conversion() {
240+
let temp = 42.0;
241+
for unit in [
242+
TemperatureUnit::Celsius,
243+
TemperatureUnit::Fahrenheit,
244+
TemperatureUnit::Kelvin,
245+
TemperatureUnit::Rankine,
246+
TemperatureUnit::Delisle,
247+
TemperatureUnit::Newton,
248+
TemperatureUnit::Reaumur,
249+
TemperatureUnit::Romer,
250+
] {
251+
assert!(approx_eq(convert_temperature(temp, unit, unit), temp));
252+
}
253+
}
254+
}

0 commit comments

Comments
 (0)