$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
数値を文字列に整形する際の落とし穴とその解決策(iOSDC2024 ルーキーズLT) / iO...
Search
glassfiber
August 23, 2024
Programming
0
810
数値を文字列に整形する際の落とし穴とその解決策(iOSDC2024 ルーキーズLT) / iOSDC Japan 2024 Formatting Floating-Point Numbers
数値を文字列に整形する際に陥った落とし穴について、具体例を交えて紹介します。
glassfiber
August 23, 2024
Tweet
Share
More Decks by glassfiber
See All by glassfiber
Pythonで広がるXcodeデバッグの世界(pixiv App Night 2025/11/06) / pixiv App Night Xcode debugger with Python
glassfiber
0
40
pixiv App Night 20240523 NumberFormatterのハマり事例
glassfiber
0
180
Other Decks in Programming
See All in Programming
GeistFabrik and AI-augmented software development
adewale
PRO
0
250
TypeScript 5.9 で使えるようになった import defer でパフォーマンス最適化を実現する
bicstone
1
1k
Level up your Gemini CLI - D&D Style!
palladius
1
170
これだけで丸わかり!LangChain v1.0 アップデートまとめ
os1ma
6
1.3k
dnx で実行できるコマンド、作ってみました
tomohisa
0
140
S3 VectorsとStrands Agentsを利用したAgentic RAGシステムの構築
tosuri13
5
270
ハイパーメディア駆動アプリケーションとIslandアーキテクチャ: htmxによるWebアプリケーション開発と動的UIの局所的適用
nowaki28
0
340
UIデザインに役立つ 2025年の最新CSS / The Latest CSS for UI Design 2025
clockmaker
17
6.7k
開発に寄りそう自動テストの実現
goyoki
1
440
Google Antigravity and Vibe Coding: Agentic Development Guide
mickey_kubo
2
130
All(?) About Point Sets
hole
0
270
AIと協働し、イベントソーシングとアクターモデルで作る後悔しないアーキテクチャ Regret-Free Architecture with AI, Event Sourcing, and Actors
tomohisa
5
18k
Featured
See All Featured
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
9
1k
Building an army of robots
kneath
306
46k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
9.8k
Optimizing for Happiness
mojombo
379
70k
4 Signs Your Business is Dying
shpigford
186
22k
Designing for humans not robots
tammielis
254
26k
How to train your dragon (web standard)
notwaldorf
97
6.4k
Thoughts on Productivity
jonyablonski
73
5k
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
120
20k
Leading Effective Engineering Teams in the AI Era
addyosmani
8
1.2k
Being A Developer After 40
akosma
91
590k
Why Our Code Smells
bkeepers
PRO
340
57k
Transcript
glass fi ber (ϐΫγϒגࣜձࣾʣ Λจࣈྻʹܗ͢Δࡍͷ མͱ݀͠ͱͦͷղܾࡦ iOSDC Japan 2024 2024/08/23
Track A ϧʔΩʔζLT
00 iOS։ൃʹ6ϒϥϯΫͷ͋Δ ΤϯδχΞ͕෮ؼͨ͠Β ͷܗͰͲϋϚΓͨ͠
༻Λύʔηϯτදࣔ͢Δ • ετϨʔδͷ༻Λύʔηϯτදࣔ • ཁ: ͋Μ·Γͯ͘ݟʹ͍͘ͷͰ͍͍ײ͡ͷܻͰग़ͯ͠ཉ͍͠ (খୈ2Ґ͘Β͍ʁ) 00 ←͜Μͳײ͡ͷͭ
༻Λύʔηϯτදࣔ͢Δ • খୈ2Ґ·Ͱදࣔ͢ΔίʔυΛॻ͖·ͨ͠ • ҰݟΑͦ͞͏ʹݟ͑·͢ ͕ 00 let usageRate =
0.12345 let value = round(usageRate * 10000) / 10000 let percentValue = value * 100 print("\(percentValue)%") 12.35% ↑0.01%=0.0001ͳͷͰ ɹখୈ5ҐͰ࢛ࣺޒೖ͢Δ
• ʹΑͬͯظ͢ΔදࣔʹͳΒͳ͍͜ͱ͕໌ • ͨͱ͕͑0.523ͷͱ͖ • খҎԼͷܻ͕ͱͯ͘ͳͬͯ͠·͏ 52.300000000000004% ʮͳΜ͔ද͕͓͔ࣔ͘͠ͳΔʯͱ͍͏ใࠂ͕ 00 let
usageRate = 0.523 let value = round(usageRate * 10000) / 10000 let percentValue = value * 100 print("\(percentValue)%")
ͳΜͰ͜͏ͳΔͷ͔ • ුಈখͷޡ͕ࠩӨڹ • 2ਐͰදݱͰ͖ͳ͍ޡؚ͕ࠩ·ΕΔ • ࢛ࣺޒೖͨͣ͠ͳͷʹɺ100ഒͨ͠Β·ͨԼҐͷܻ͕ݱΕΔ… • ͱΓ͋͑ͣɺύʔηϯτද͍ࣔͨ͠ͱ͖ʹ100ഒͯ͠ͳΜͱ͔͠Α͏ͱ ͢ΔͷΑ͘ͳͦ͞͏
00
ܻΛࢦఆͨ͠ΒΑ͍ͷͰ • String(format:)ͰখҎԼͷܻΛࢦఆͨ͠ΒͲ͏Ͱ͠ΐ͏ • ݁Ռɺܻ͕ݻఆͳͷͰɺඌʹ0͕ͭ͘ • ͜ΕͰ͍͍͕ɺ͏ͪΐͬͱ͍͍ײ͡ʹ͍ͨ͠ • ඌͷ0ͳ͍ͨ͘͠ 00
let usageRate = 0.523 let value = round(usageRate * 10000) / 10000 print("\(String(format: "%.2f", value * 100))%") 52.30% ↑খҎԼ2ܻ
ͷܗͱ͍͑NumberFormatter • numberStyle = .percent ͱ͢Ε100ഒͤͣͱύʔηϯτදهʹͳΔ • ͢Β͍͠ 00 let
usageRate = 0.523 let formatter = NumberFormatter() formatter.numberStyle = .percent formatter.maximumFractionDigits = 2 if let str = formatter.string(from: NSNumber(value: usageRate)) { print(str) } 52.3% ←খҎԼ࠷େ2ܻ
খҎԼ2ܻʹͰ͖ͨɺ͚Ͳ • ཁ͕͖ͨ • 12.34%ͱ͍͏ͷ͍ɺͰे • Ͱ0.12%͜ͷ··খҎԼ2ܻग़ͯཉ͍͠ • →ʹΑܻͬͯΛ͍͍ײ͡ʹΓସ͍͑ͨ 00
let formatter = NumberFormatter() formatter.numberStyle = .percent func myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = 2 return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) 12.34% 1.23% 0.12%
ͰɺܻΛΓସ͑ͯΈΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent func
myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = switch value { case ..<1: 2 case ..<10: 1 default: 0 } return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) ←ͷൣғͰ߹͚ܻͯ͠ΛΓସ͑Δ 1%ະຬ / 1%Ҏ্10%ະຬ / 10%Ҏ্
ͰɺܻΛΓସ͑ͯΈΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent func
myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = switch value { case ..<1: 2 case ..<10: 1 default: 0 } return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) ←ͷൣғͰ߹͚ܻͯ͠ΛΓସ͑Δ 1%ະຬ / 1%Ҏ্10%ະຬ / 10%Ҏ্ 12.34% 1.23% 0.12% มΘͬͯͳ͍…ʁ
ͰɺܻΛΓସ͑ͯΈΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent func
myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = switch value { case ..<1: 2 case ..<10: 1 default: 0 } return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) 12.34% 1.23% 0.12% มΘͬͯͳ͍…ʁ ←ˋͷͰൺֱ͢Δؒҧ͍ (Α͘Δ)
ͰɺܻΛΓସ͑ͯΈΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent func
myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = switch value { case ..<0.01: 2 case ..<0.1: 1 default: 0 } return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) ←ͷൣғͰ߹͚ܻͯ͠ΛΓସ͑Δ 1%ະຬ / 1%Ҏ্10%ະຬ / 10%Ҏ্
ͰɺܻΛΓସ͑ͯΈΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent func
myFormat(_ value: Double) -> String { formatter.maximumFractionDigits = switch value { case ..<0.01: 2 case ..<0.1: 1 default: 0 } return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) 12% 1.2% 0.12% ͍͍ײ͡ ←ͷൣғͰ߹͚ܻͯ͠ΛΓସ͑Δ 1%ະຬ / 1%Ҏ্10%ະຬ / 10%Ҏ্
ද͍͍ࣔײ͚ͩ͡Ͳɺίʔυ͕ؾʹೖΒͳ͍ • switchͰ͚ΔͷͳΜ͔ΠϚΠν 00 formatter.maximumFractionDigits = switch value { case
..<0.01: 2 case ..<0.1: 1 default: 0 }
Ͳ͏͍ͨ͠ͷ͔ߟ͑ͯΈΔ • ༗ޮࣈ͕2ܻɺͱ͍͏͜ͱͰΑͦ͞͏…ʁ • NumberFomatterʹ༗ޮܻΛࢦఆ͢Δػೳ͕͋Δ (maximumSigni fi cantDigits) 00 →
→ → 12% 1.2% 0.12% 0.12345 0.012345 0.0012345 ͜ͷͷͱ͖ ͜͏͍ͨ͠
༗ޮࣈ2ܻͰܗ͢Δ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent formatter.usesSignificantDigits
= true formatter.maximumSignificantDigits = 2 func myFormat(_ value: Double) -> String { return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) 12% 1.2% 0.12% ←༗ޮܻ2ܻʹઃఆ Αͦ͞͏…ʁ
খ͍͞ͷͱ͖ʹ͘ͳͬͯ͠·͏ͱࢦఠ͞ΕΔ 00 let formatter = NumberFormatter() formatter.numberStyle = .percent formatter.usesSignificantDigits
= true formatter.maximumSignificantDigits = 2 func myFormat(_ value: Double) -> String { return formatter.string(from: NSNumber(value: value)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) print(myFormat(0.00012345)) print(myFormat(0.00000012345)) 12% 1.2% 0.12% 0.012% 0.000012%
খ͍͞ͷͱ͖ʹ͘ͳͬͯ͠·͏ • খ͍͞ͷͱ͖ʹ͔ͬ͠Γ༗ޮࣈ2ܻͰग़ΔͷͰ͘ͳΔ • খ෦ͷܻࢦఆ(maximumFractionDigits) จࣈྻͷ੍͕͞ݶ͞ ΕΔ͕ɺ༗ޮܻࢦఆ(maximumSigni fi cantDigits) ͦ͏Ͱͳ͍
• ͦΕͦ͏ 00 print(myFormat(0.00012345)) print(myFormat(0.00000012345)) 0.012% 0.000012%
খ͍ܻ͞Λ࢛ࣺޒೖͯ͠ղܾ • ࠷ॳʹ͍ͬͯͨɺখୈ5ҐͰͷ࢛ࣺޒೖΛ࠶ͼಋೖ͠·͢ • Α͏͍͍͘ײ͡ʹͰ͖·ͨ͠ 00 func myFormat(_ value: Double)
-> String { let roundedValue = round(value * 10000) / 10000 return formatter.string(from: NSNumber(value: roundedValue)) ?? "" } print(myFormat(0.12345)) print(myFormat(0.012345)) print(myFormat(0.0012345)) print(myFormat(0.00012345)) print(myFormat(0.00000012345)) ←খୈ5ҐͰ࢛ࣺޒೖ 12% 1.2% 0.12% 0.01% 0%
func test_༻༰ྔ͕0ͷͱ͖ʹ0ύʔηϯτදهʹͳΔ() { let usage = 0 let limit =
1000000 * 1000 * 1000 let user = testUser(usage, limit) XCTAssertEqual(user.storageUsageFormatted, "0%") } func test_༻༰ྔ͕0_01ύʔηϯτະຬͷͱ͖ʹ0ύʔηϯτදهʹͳΔ let usage = 12 * 1000 * 1000 let limit = 1000000 * 1000 * 1000 let user = testUser(usage, limit) XCTAssertEqual(user.storageUsageFormatted, "0%") } func test_༻༰ྔ͕0_1ύʔηϯτະຬͷͱ͖ʹখ2Ґ·ͰͷදهʹͳΔ let usage = 123 * 1000 * 1000 let limit = 1000000 * 1000 * 1000 let user = testUser(usage, limit) XCTAssertEqual(user.storageUsageFormatted, "0.01%") } func test_༻༰ྔ͕1ύʔηϯτະຬͷͱ͖ʹখ2Ґ·ͰͷදهʹͳΔ() let usage = 1234 * 1000 * 1000 let limit = 1000000 * 1000 * 1000 let user = testUser(usage, limit) XCTAssertEqual(user.storageUsageFormatted, "0.12%") } func test_༻༰ྔ͕10ύʔηϯτະຬͷͱ͖ʹখ1Ґ·ͰͷදهʹͳΔ( ςετΛॻ͍ͯ҆৺͢Δ • Ͳ͏ͳͬͯ΄͍͔͠ΛͪΌ Μͱॻ͍ͯ֬ೝ͢Δ • ࣮ࡍͷετϨʔδ༰ྔͰද ࣔΛ֬ೝ͢Δͷඇݱ࣮త • ϋϚΔͳͲΛཏྻ͠ ͯɺظ͢ΔදࣔʹͳΔ͜ ͱΛ֬ೝ
·ͱΊ • ුಈখͷѻ͍͍͠ • Λύʔηϯτදهʹܗ͢Δࡍʹ100ഒͯ͠Ͳ͏ʹ͔͠Α͏ͱ͠ ͍͚ͯͳ͍ • NumberFormatterͳͲΛ͍ͬͯͩ͘͞ • ༗ޮܻͷࢦఆศར͕ͩɺจࣈྻͷ͞Λ੍ݶͯ͘͠Εͳ͍ͷͰҙ
͕ඞཁ • maximumFractionDigits ͱಉ͡Α͏ͳͷͱࢥͬͯ͠·͏ϫφ • ςετΛॻ͍ͯ҆৺͠Α͏ 00
ࣗݾհ glass fi ber ϐΫγϒגࣜձࣾ ৽نࣄۀ෦ iOSΤϯδχΞ • 20238݄ த్ೖࣾ
• Pastelaͱ͍͏͓ֆඳ͖ΞϓϦͷ։ൃʹैࣄ • લ৬ήʔϜ։ൃऀ • C++, C#Λओʹ༻ • 6લ·ͰiOS༻ήʔϜ࡞͍ͬͯͨ(iOS3ʙ7ͷ͜Ζ) • ϒϥϯΫΛຒΊΔͨΊमߦத 00
00 ͓·͚
ුಈখͷதΛݟΔ • ුಈখ: IEEE754 (Double : ഒਫ਼ɺFloat: ୯ਫ਼) • ුಈখͷܭࢉΛϏοτ୯ҐͰݟΕΔπʔϧ
• IEEE 754 Calculator (http://weitz.de/ieee/) 00
ුಈখͷதΛݟΔ • SwiftͰ .bitPatternͰුಈখͷදݱΛ ֬ೝͰ͖Δ • .exponentBitPatternͱ.signi fi candBitPatternͰࢦ෦ͱԾ෦Λݸผ ʹݟΔ͜ͱՄೳ
00 print("\(String(0.523.bitPattern, radix: 2))") print("\(String(0.523.exponentBitPattern, radix: 2))") print("\(String(0.523.significandBitPattern, radix: 2))") 11111111100000101111000110101001111110111110011101101100100011 1111111110 101111000110101001111110111110011101101100100011
None