ByteCountFormatter iOS Swift - swift

Convert Bytes values into GB/MB/KB, i am using ByteCountFormatter. Example code below.
func converByteToGB(_ bytes:Int64) -> String {
let formatter:ByteCountFormatter = ByteCountFormatter()
formatter.countStyle = .binary
return formatter.string(fromByteCount: Int64(bytes))
}
Now, my requirement is it should show only one digit after decimal point.
Example for 1.24 GB => 1.2 GB, not like 1.24 GB. force it for single digit after apply floor or ceil function.

ByteCountFormatter is not capable to show only one digit after decimal point. It shows by default 0 fraction digits for bytes and KB; 1 fraction digits for MB; 2 for GB and above. If isAdaptive is set to false it tries to show at least three significant digits, introducing fraction digits as necessary.
ByteCountFormatter also trims trailing zeros. To disable set zeroPadsFractionDigits to true.
I have adapted How to convert byte size into human readable format in java? to do what you want:
func humanReadableByteCount(bytes: Int) -> String {
if (bytes < 1000) { return "\(bytes) B" }
let exp = Int(log2(Double(bytes)) / log2(1000.0))
let unit = ["KB", "MB", "GB", "TB", "PB", "EB"][exp - 1]
let number = Double(bytes) / pow(1000, Double(exp))
return String(format: "%.1f %#", number, unit)
}
Notice this will format KB and MB differently than ByteCountFormatter. Here is a modification that removes trailing zero and does not show fraction digits for KB and for numbers larger than 100.
func humanReadableByteCount(bytes: Int) -> String {
if (bytes < 1000) { return "\(bytes) B" }
let exp = Int(log2(Double(bytes)) / log2(1000.0))
let unit = ["KB", "MB", "GB", "TB", "PB", "EB"][exp - 1]
let number = Double(bytes) / pow(1000, Double(exp))
if exp <= 1 || number >= 100 {
return String(format: "%.0f %#", number, unit)
} else {
return String(format: "%.1f %#", number, unit)
.replacingOccurrences(of: ".0", with: "")
}
}
Be also aware that this implementation does not take Locale into account. For example some locales use comma (",") instead of dot (".") as decimal separator.

Related

How to print all the digits in a large number of 10 power 25 in swift?

I have been working on a hacker rank problem where I have to print a number which is a factorial of 25. Here is the code I used.
func extraLongFactorials(n: Int) -> Void {
let factorialNumber = factorial(number: n)
var arrayForStorage: [Int] = []
var loop = factorialNumber
while (loop > 0) {
let digit = loop.truncatingRemainder(dividingBy: 10)
arrayForStorage.append(Int(digit))
loop /= 10
}
arrayForStorage = arrayForStorage.reversed()
var returnString = ""
for element in arrayForStorage {
returnString = "\(returnString)\(element)"
}
print(returnString)
}
func factorial(number: Int) -> Double {
if number == 0 || number == 1 {
return 1
} else if number == 2 {
return 2
} else {
return Double(number) * factorial(number: number - 1)
}
}
But when I try to print the factorial number it just prints 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015511210043330982408266888 when it should print
15511210043330985984000000.
I think for a Double number truncatingRemainder(dividingBy: 10) method is not giving me the exact number of the remainder. Because when I tried to print the truncatingRemainder of 15511210043330985984000000 it is giving me as 8. Here is the code.
let number: Double = 15511210043330985984000000
print(number.truncatingRemainder(dividingBy: 10))
So finally I didn't find any solution for the problem of how to split the large number and add it into an array. Looking forward for the solution.
Type Double stores a number as a mantissa and an exponent. The mantissa represents the significant figures of the number, and the exponent represents the magnitude of the number. A Double can only represent about 16 significant figures, and your number has 26 digits, so you can't accurately store 15511210043330985984000000 in a Double.
let number1: Double = 15511210043330985984000000
let number2: Double = 15511210043330985984012345
if number1 == number2 {
print("they are equal")
}
they are equal
You will need another approach to find large factorials like the one shown in this answer.

Swift 3 : Negative Int to hexadecimal

Hy everyone,
I need to transform a Int to its hexadecimal value.
Example : -40 => D8
I have a working method for positive (or unsigned) Int but it doesn't work as expected with negatives. Here's my code.
class func encodeHex(data:[Int]) -> String {
let hexadecimal = data.reduce("") { (string , element) in
var append = String(element, radix:16 , uppercase : false)
if append.characters.count == 1 {
append = "0" + append
}
return string + append
}
return hexadecimal
}
If I pass -40 I get -28.
Can anyone help ? Thanks :)
I assume from your existing code that all integers are in the range
-128 ... 127. Then this would work:
func encodeHex(data:[Int]) -> String {
return data.map { String(format: "%02hhX", $0) }.joined()
}
The "%02hhX" format prints the least significant byte of the
given integer in base 16 with 2 digits.
Example:
print(encodeHex(data: [40, -40, 127, -128]))
// 28D87F80
D8 is the last byte of binary representation of -40. The remaining three bytes are all FFs.
If you are looking for a string that represents only the last byte, you can obtain by first converting your number to unsigned 8-bit integer, and then converting it to hex, like this:
let x = UInt8(bitPattern:Int8(data))
let res = String(format:"%02X", x)

Rounding numbers in swift

In Swift, I need to be able to round numbers based on their value. If a number is whole, which just ".0" after it, I need to convert it to an integer, and if the number has digits after the decimal that is greater than 2 digits, I need to round it to 2 digits.
For example:
1.369352 --> 1.37
7.75 --> 7.75
2.0 --> 2
How can I check my numbers and round them according to these rules?
Something like this should be good?
func formatNumber (number: Double) -> String? {
let formatter = NSNumberFormatter()
formatter.maximumFractionDigits = 2
let formattedNumberString = formatter.stringFromNumber(number)
return formattedNumberString?.stringByReplacingOccurrencesOfString(".00", withString: "")
}
formatNumber(3.25) // 3.25
formatNumber(3.00) // 3
formatNumber(3.25678) // 3.26
this function returns a string of the result needed.
func roundnumber(roundinput:Double) ->String{
var roundoutputint=0
var roundoutputfloat=0.0
if (roundinput - floor(roundinput) < 0.00001) { // 0.000001 can be changed depending on the level of precision you need
//integer
roundoutputint = Int(round(roundinput))
return String(roundoutputint)
}
else {
//not integer
//roundoutputfloat=round(10 * roundinput) / 10
return String(format:"%.2f",roundinput)
}
}
for example:
roundnumber(1.3693434) //returns "1.37"
roundnumber(7.75) //returns "7.75"
roundnumber(2.0) // returns "2"

Formatting decimal places with unknown number

I'm printing out a number whose value I don't know. In most cases the number is whole or has a trailing .5. In some cases the number ends in .25 or .75, and very rarely the number goes to the thousandths place. How do I specifically detect that last case? Right now my code detects a whole number (0 decimal places), exactly .5 (1 decimal), and then reverts to 2 decimal spots in all other scenarios, but I need to go to 3 when it calls for that.
class func getFormattedNumber(number: Float) -> NSString {
var formattedNumber = NSString()
// Use the absolute value so it works even if number is negative
if (abs(number % 2) == 0) || (abs(number % 2) == 1) { // Whole number, even or odd
formattedNumber = NSString(format: "%.0f", number)
}
else if (abs(number % 2) == 0.5) || (abs(number % 2) == 1.5) {
formattedNumber = NSString(format: "%.1f", number)
}
else {
formattedNumber = NSString(format: "%.2f", number)
}
return formattedNumber
}
A Float uses a binary (IEEE 754) representation and cannot represent
all decimal fractions precisely. For example,
let x : Float = 123.456
stores in x the bytes 42f6e979, which is approximately
123.45600128173828. So does x have 3 or 14 fractional digits?
You can use NSNumberFormatter if you specify a maximum number
of decimal digits that should be presented:
let fmt = NSNumberFormatter()
fmt.locale = NSLocale(localeIdentifier: "en_US_POSIX")
fmt.maximumFractionDigits = 3
fmt.minimumFractionDigits = 0
println(fmt.stringFromNumber(123)!) // 123
println(fmt.stringFromNumber(123.4)!) // 123.4
println(fmt.stringFromNumber(123.45)!) // 123.45
println(fmt.stringFromNumber(123.456)!) // 123.456
println(fmt.stringFromNumber(123.4567)!) // 123.457
Swift 3/4 update:
let fmt = NumberFormatter()
fmt.locale = Locale(identifier: "en_US_POSIX")
fmt.maximumFractionDigits = 3
fmt.minimumFractionDigits = 0
print(fmt.string(for: 123.456)!) // 123.456
You can use %g to suppress trailing zeros. Then I think you do not need to go through the business of determining the number of places. Eg -
var num1:Double = 5.5
var x = String(format: "%g", num1) // "5.5"
var num2:Double = 5.75
var x = String(format: "%g", num2) // "5.75"
Or this variation where the number of places is specified. Eg -
var num3:Double = 5.123456789
var x = String(format: "%.5g", num3) // "5.1235"
My 2 cents ;) Swift 3 ready
Rounds the floating number and strips the trailing zeros to the required minimum/maximum fraction digits.
extension Double {
func toString(minimumFractionDigits: Int = 0, maximumFractionDigits: Int = 2) -> String {
let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.minimumFractionDigits = minimumFractionDigits
formatter.maximumFractionDigits = maximumFractionDigits
return formatter.string(from: self as NSNumber)!
}
}
Usage:
Double(394.239).toString() // Output: 394.24
Double(394.239).toString(maximumFractionDigits: 1) // Output: 394.2
If you want to print a floating point number to 3 decimal places, you can use String(format: "%.3f"). This will round, so 0.10000001 becomes 0.100, 0.1009 becomes 0.101 etc.
But it sounds like you don’t want the trailing zeros, so you might want to trim them off. (is there a way to do this with format? edit: yes, g as #simons points out)
Finally, this really shouldn’t be a class function since it’s operating on primitive types. Better to either make it a free function, or perhaps extend Double/Float:
extension Double {
func toString(#decimalPlaces: Int)->String {
return String(format: "%.\(decimalPlaces)g", self)
}
}
let number = -0.3009
number.toString(decimalPlaces: 3) // -0.301

Split a double by dot to two numbers

So I am trying to split a number in swift, I have tried searching for this on the internet but have had no success. So first I will start with a number like:
var number = 34.55
And from this number, I want to create two separate number by splitting from the dot. So the output should be something like:
var firstHalf = 34
var secondHalf = 55
Or the output can also be in an array of thats easier. How can I achieve this?
The easiest way would be to first cast the double to a string:
var d = 34.55
var b = "\(d)" // or String(d)
then split the string with the global split function:
var c = split(b) { $0 == "." } // [34, 55]
You can also bake this functionality into a double:
extension Double {
func splitAtDecimal() -> [Int] {
return (split("\(self)") { $0 == "." }).map({
return String($0).toInt()!
})
}
}
This would allow you to do the following:
var number = 34.55
print(number.splitAtDecimal()) // [34, 55]
Well, what you have there is a float, not a string. You can't really "split" it, and remember that a float is not strictly limited to 2 digits after the separator.
One solution is :
var firstHalf = Int(number)
var secondHalf = Int((number - firstHalf) * 100)
It's nasty but it'll do the right thing for your example (it will, however, fail when dealing with numbers that have more than two decimals of precision)
Alternatively, you could convert it into a string and then split that.
var stringified = NSString(format: "%.2f", number)
var parts = stringifed.componentsSeparatedByString(".")
Note that I'm explicitly calling the formatter here, to avoid unwanted behavior of standard float to string conversions.
Add the following extension:
extension Double {
func splitIntoParts(decimalPlaces: Int, round: Bool) -> (leftPart: Int, rightPart: Int) {
var number = self
if round {
//round to specified number of decimal places:
let divisor = pow(10.0, Double(decimalPlaces))
number = Darwin.round(self * divisor) / divisor
}
//convert to string and split on decimal point:
let parts = String(number).components(separatedBy: ".")
//extract left and right parts:
let leftPart = Int(parts[0]) ?? 0
let rightPart = Int(parts[1]) ?? 0
return(leftPart, rightPart)
}
Usage - Unrounded:
let number:Double = 95.99999999
let parts = number.splitIntoParts(decimalPlaces: 3, round: false)
print("LeftPart: \(parts.leftPart) RightPart: \(parts.rightPart)")
Outputs:
LeftPart: 95 RightPart: 999
Usage Rounded:
let number:Double = 95.199999999
let parts = number.splitIntoParts(decimalPlaces: 1, round: true)
Outputs:
LeftPart: 95 RightPart: 2
Actually, it's not possible to split a double by dot into two INTEGER numbers. All earlier offered solutions will produce a bug in nearly 10% of cases.
Here's why:
The breaking case will be numbers with decimal parts starting with one or more zeroes, for example: 1.05, 11.00698, etc.
Any decimal part that starts with one or more zeroes will have those zeroes discarded when converted to integers. The result of those conversions:
1.05 will become (1, 5)
11.00698 will become (11, 698)
An ugly and hard to find bug is guaranteed...
The only way to meaningfully split a decimal number is to convert it to (Int, Double). Below is a simple extension to Double that does that:
extension Double {
func splitAtDecimal() -> (Int, Double) {
let whole = Int(self)
let decimal = self - Darwin.floor(self)
return (whole, decimal)
}
}