There's a "Clamp" function from a library of Ray Wenderlich class's - SKTUtils to be exact. This clamp function is written in shorthand but in a way that I can't seem to understand. This clamps purpose is to limit a position to an area - the games "camera" follows the _player.position, while making sure the the player never sees the nothingness outside the game map. Here's the function:
CGFloat Clamp(CGFloat value, CGFloat min, CGFloat max)
{
return value < min ? min : value > max ? max : value;
}
Here is the method it's used in, which the method it self gets used inside 'didFinishUpdate' method:
-(CGPoint)pointToCenterViewOn:(CGPoint)centerOn
{
CGFloat x = Clamp(centerOn.x, self.size.width/2, _backgroundLayer.layerSize.width - self.size.width/2); //Value, Min, Max.
CGFloat y = Clamp(centerOn.y, self.size.height/2, _backgroundLayer.layerSize.height - self.size.height/2);
return CGPointMake(-x, -y);
}
-(void)didFinishUpdate
{
_worldNode.position = [self centerViewOnPoint:_player.position];
}
Can someone explain this?
value < min ? min : value > max ? max : value
I could only partially understand the shorthands beginning:
if (value < min)
{
value = min;
}
else if (value > min)
{
value > max??????
}
Here is the explanation of value < min ? min : value > max ? max : value
if (value < min)
{
return min
}
else
{
if (value > max)
{
return max
}
else
{
return value
}
}
Operator precedence is partially involved here. This would be made a lot nicer with some parentheses to aid reading. The comparison operators bind tighter than the ternary conditional, so you have:
(value < min) ? min : ((value > max) ? max : value)
From there it's just evaluated left-to-right. The only tricky bit is that the else branch of the first conditional operator is itself another conditional operator. This would be the equivalent of an else if were you to expand it. The else branch of the second conditional is thus the else for the whole expression.
To convert this to if statements, then, you would do:
CGFloat retVal;
if( value < min ){
retVal = min;
}
else if( value > max ){
retVal = max;
}
else {
retVal = value;
}
return retVal;
You might also prefer this way to clamp a value:
MAX(min_limit, MIN(value, max_limit))
which uses the MAX and MIN macros to evaluate to the lower of max_limit or value and the higher of that or min_limit, producing a result in the range between min_limit and max_limit (inclusive). The effect is the same; I think that's easier to read.
Related
In Dart, is there a simple way to check whether the sum of a list will produce a 'real' value (a value that doesn't overflow or underflow)?
Examples:
overflowSafeSum([0,1,2]) //3
overflowSafeSum([1,9223372036854775807]) //Over
overflowSafeSum([-1,-9223372036854775808]) //Under
I'm new to dart, this is the best I got right now:
import 'dart:math' show pow;
enum Overflow {
over,
under,
}
void main() {
//idea: Iterate through the elements of a list and add them,
//each time the sum overflows: increase overflowCounter by 1
//each time the sum underflows: decrease overflowCounter by 1
//if all the elements have been added and the overflowCounter == 0, the sum must be real
overflowSafeSum(List<int> userList) {
var sum = 0, overflowCounter = 0;
for (int index = 0, nextTerm;
index < userList.length;
index++, sum += nextTerm) {
nextTerm = userList[index];
if (sum.sign != nextTerm.sign) {
continue; //adding a postive and negative can't overflow or underflow
} else if (sum >= 0 && nextTerm >= 0) {
if ((sum + nextTerm) < 0) overflowCounter++;
} else {
if ((sum + nextTerm) >= 0) overflowCounter--;
}
}
if (overflowCounter == 0) {
return sum;
} else if (overflowCounter > 0) {
return Overflow.over;
} else {
return Overflow.under;
}
}
var myList = [1,0,(pow(2,63)-1).toInt()];
print(overflowSafeSum(myList)); //Overflow.over
}
(To be pedantic: "underflow" is not negative overflow. Overflow occurs when the magnitude of a number is too large to be represented, regardless of sign. Underflow is an issue with floating-point operations where the magnitude of a number is too small (too close to 0) to be represented.)
You can't generally detect overflow with Dart ints since Dart for the web is transpiled to JavaScript, where ints are backed by JavaScript numbers (IEEE-754 double-precision floating-point values). If you instead use Int32 or Int64 from package:fixnum (or if you restrict yourself to the Dart VM), then you could make a helper function like:
class OverflowException implements Exception {
OverflowException({this.positive = true});
bool positive;
}
Int64 checkedAdd(Int64 a, Int64 b) {
var sum = a + b;
if (a > 0 && b > 0 && sum < 0) {
throw OverflowException(positive: true);
}
if (a < 0 && b < 0 && sum > 0) {
throw OverflowException(positive: false);
}
return sum;
}
From there, you could trivially add a function that calls it in a loop:
Int64 overflowSafeSum(Iterable<int> numbers) {
var sum = Int64(0);
for (var number in numbers) {
sum = checkedAdd(sum, Int32(number));
}
return sum;
}
or if you prefer using Iterable.fold:
Int64 overflowSafeSum(Iterable<int> numbers) =>
numbers.fold<Int64>(Int64(0), (sum, i) => checkedAdd(sum, Int64(i)));
Take a look at this code. I want to extract the minimum and maximum ask prices and print these out as part of the statistics:
double OrderBook::getHighPrice(std::vector<OrderBookEntry>& orders)
{
// find the highest value in array of value
double max = orders[0].price;
for (OrderBookEntry& e : orders)
{
if (e.price > max)
{
max = e.price;
}
return max;
}
}
double OrderBook::getLowPrice(std::vector<OrderBookEntry>& orders)
{
// find the lowest value in array of value
double min = orders[0].price;
for (OrderBookEntry& e : orders)
{
if (e.price < min)
{
min = e.price;
}
return min;
}
}
Here is an extract of the output I am getting, max and min are the same:
Product: BTC/USDT
Asks seen: 50
Max ask: 5352
Min ask: 5352
Product: DOGE/BTC
Asks seen: 50
Max ask: 3.1e-07
Min ask: 3.1e-07
Product: DOGE/USDT
Asks seen: 50
Max ask: 0.00165524
Min ask: 0.00165524
Product: ETH/BTC
Asks seen: 50
Max ask: 0.0218909
Min ask: 0.0218909
Product: ETH/USDT
Asks seen: 50
Max ask: 117.329
Min ask: 117.329
1: Print help
2: Print exchange stats
3: Make an offer
4: Make a bid
5: Print wallet
6: Continue
==============
Current time is: 2020/03/17 17:01:24.884492
Type in 1-6
I want to make sure that max and min are different and correct. Where am I going wrong? Please help.
You're returning from inside the for loops. You need to wait until all the prices have been checked and then return after the for loops:
double OrderBook::getHighPrice(std::vector<OrderBookEntry>& orders)
{
// find the highest value in array of value
double max = orders[0].price;
for (OrderBookEntry& e : orders)
{
if (e.price > max)
{
max = e.price;
}
}
return max;
}
double OrderBook::getLowPrice(std::vector<OrderBookEntry>& orders)
{
// find the lowest value in array of value
double min = orders[0].price;
for (OrderBookEntry& e : orders)
{
if (e.price < min)
{
min = e.price;
}
}
return min;
}
I recently started working with Dart, and was trying to format a countdown clock with numbers in a per-second precision.
When counting down time, there's often a precise-yet-imperfect way of representing the time - so if I started a Duration at 2 minutes, and asked to show the current time after one second has elapsed, it is almost guaranteed that the precision of the timer will report at 1:58:999999 (example), and if use Duration.inSeconds() to emit the value, it will be 118 (seconds) which is due to how the ~/ operator works, since it's rounding down to integers based on the Duration's microseconds.
If I render the value as a clock, I'll see the clock go from "2:00" to "1:58" after one second, and will end up displaying "0:00" twice, until the countdown is truly at 0:00:00.
As a human, this appears like the clock is skipping, so I figured since the delta is so small, I should round up to the nearest second, and that would be accurate enough for a countdown timer, and handle the slight imprecision measured in micro/milli-seconds to better serve the viewer.
I came up with this secondRounder approach:
Duration secondRounder(Duration duration) {
int roundedDuration;
if (duration.inMilliseconds > (duration.inSeconds * 1000)) {
roundedDuration = duration.inSeconds + 1;
} else {
roundedDuration = duration.inSeconds;
}
return new Duration(seconds: roundedDuration);
}
This can also be run in this DartPad: https://dartpad.dartlang.org/2a08161c5f889e018938316237c0e810
As I'm yet unfamiliar with all of the methods, I've read through a lot of the docs, and this is the best I've come up with so far. I think I was looking for a method that might looks like:
roundedDuration = duration.ceil(nearest: millisecond)
Is there a better way to go about solving this that I haven't figured out yet?
You can "add" your own method to Duration as an extension method:
extension RoundDurationExtension on Duration {
/// Rounds the time of this duration up to the nearest multiple of [to].
Duration ceil(Duration to) {
int us = this.inMicroseconds;
int toUs = to.inMicroseconds.abs(); // Ignore if [to] is negative.
int mod = us % toUs;
if (mod != 0) {
return Duration(microseconds: us - mod + toUs);
}
return this;
}
}
That should allow you to write myDuration = myDuration.ceil(Duration(seconds: 1)); and round the myDuration up to the nearest second.
The best solution according to the documentation is to use .toStringAsFixed() function
https://api.dart.dev/stable/2.4.0/dart-core/num/toStringAsFixed.html
Examples from the Documentation
1.toStringAsFixed(3); // 1.000
(4321.12345678).toStringAsFixed(3); // 4321.123
(4321.12345678).toStringAsFixed(5); // 4321.12346
123456789012345678901.toStringAsFixed(3); // 123456789012345683968.000
1000000000000000000000.toStringAsFixed(3); // 1e+21
5.25.toStringAsFixed(0); // 5
Another more flexible option can be...
You can use this function to roundup the time.
DateTime alignDateTime(DateTime dt, Duration alignment,
[bool roundUp = false]) {
assert(alignment >= Duration.zero);
if (alignment == Duration.zero) return dt;
final correction = Duration(
days: 0,
hours: alignment.inDays > 0
? dt.hour
: alignment.inHours > 0
? dt.hour % alignment.inHours
: 0,
minutes: alignment.inHours > 0
? dt.minute
: alignment.inMinutes > 0
? dt.minute % alignment.inMinutes
: 0,
seconds: alignment.inMinutes > 0
? dt.second
: alignment.inSeconds > 0
? dt.second % alignment.inSeconds
: 0,
milliseconds: alignment.inSeconds > 0
? dt.millisecond
: alignment.inMilliseconds > 0
? dt.millisecond % alignment.inMilliseconds
: 0,
microseconds: alignment.inMilliseconds > 0 ? dt.microsecond : 0);
if (correction == Duration.zero) return dt;
final corrected = dt.subtract(correction);
final result = roundUp ? corrected.add(alignment) : corrected;
return result;
}
and then use it the following way
void main() {
DateTime dt = DateTime.now();
var newDate = alignDateTime(dt,Duration(minutes:30));
print(dt); // prints 2022-01-07 15:35:56.288
print(newDate); // prints 2022-01-07 15:30:00.000
}
Is there a way with NumberFormat to display :
'15' if double value is 15.00
'15.50' if double value is 15.50
Thanks for your help.
Actually, I think it's easier to go with truncateToDouble() and toStringAsFixed() and not use NumberFormat at all:
n.toStringAsFixed(n.truncateToDouble() == n ? 0 : 2);
So for example:
main() {
double n1 = 15.00;
double n2 = 15.50;
print(format(n1));
print(format(n2));
}
String format(double n) {
return n.toStringAsFixed(n.truncateToDouble() == n ? 0 : 2);
}
Prints to console:
15
15.50
Edit: The solution posted by Martin seens to be a better one
I don't think this can be done directly. You'll most likely need something like this:
final f = new NumberFormat("###.00");
String format(num n) {
final s = f.format(n);
return s.endsWith('00') ? s.substring(0, s.length - 3) : s;
}
Not very easily. Interpreting what you want as printing zero decimal places if it's an integer value and precisely two if it's a float, you could do
var forInts = new NumberFormat();
var forFractions = new NumberFormat();
forFractions.minimumFractionDigits = 2;
forFractions.maximumFractionDigits = 2;
format(num n) =>
n == n.truncate() ? forInts.format(n) : forFractions.format(n);
print(format(15.50));
print(format(15.0));
But there's little advantage in using NumberFormat for this unless you want the result to print differently for different locales.
Maybe you don't want use NumberFormat:
class DoubleToString {
String format(double toFormat) {
return (toFormat * 10) % 10 != 0 ?
'$toFormat' :
'${toFormat.toInt()}';
}
}
A variant of double value formatting:
void main (){
final n1 = 15.00;
final n2 = 15.50;
print(format(n1));
print(format(n2));
}
String format(double n) {
final fraction = n - n.toInt();
if (fraction == 0.0) {
return n.toString();
}
var twoDigitFraction = (fraction * 100).truncateToDouble().toInt();
return '${n.toInt()}.$twoDigitFraction';
}
This will work.
main() {
double n1 = 15.00;
double n2 = 15.50;
print(_formatDecimal(n1));
print(_formatDecimal(n2));
}
_formatDecimal(double value) {
if (value % 1 == 0) return value.toStringAsFixed(0).toString();
return value.toString();
}
Output:
15
15.5
An alternate solution, working on the string output of NumberFormat:
final f = NumberFormat("###.00");
print(f.format(15.01).replaceAll('.00', ''));
print(f.format(15.00).replaceAll('.00', ''));
Here is a flexible function that nicely rounds and removes trailing zeros after the decimal point to resolve double's imperfections. This doesn't handle the strictly 0 or 2 decimal points scenario from the question, but rather is a more general formatting for double numbers that may be useful for others to consider.
The verbose value can be changed to fit precision needs.
void main() {
for (double i = 0; i < 10; i += 0.3) {
print(i);
print(_formatDouble(i));
}
}
//Creates nicely formatted number string without trailing decimal zeros.
String _formatDouble(double value) {
//this also rounds (so 0.8999999999999999 becomes '0.9000')
var verbose = value.toStringAsFixed(4);
var trimmed = verbose;
//trim all trailing 0's after the decimal point (and the decimal point if applicable)
for (var i = verbose.length - 1; i > 0; i--) {
if (trimmed[i] != '0' && trimmed[i] != '.' || !trimmed.contains('.')) {
break;
}
trimmed = trimmed.substring(0, i);
}
return trimmed;
}
prints output:
0
0
0.3
0.3
0.6
0.6
0.8999999999999999
0.9
1.2
1.2
1.5
1.5
1.8
1.8
2.1
2.1
2.4
2.4
2.6999999999999997
2.7
2.9999999999999996
3
3.2999999999999994
3.3
3.599999999999999
3.6
3.899999999999999
3.9
4.199999999999999
4.2
4.499999999999999
4.5
4.799999999999999
4.8
5.099999999999999
5.1
5.399999999999999
5.4
5.699999999999998
5.7
5.999999999999998
6
6.299999999999998
6.3
6.599999999999998
6.6
6.899999999999998
6.9
7.1999999999999975
7.2
7.499999999999997
7.5
7.799999999999997
7.8
8.099999999999998
8.1
8.399999999999999
8.4
8.7
8.7
9
9
9.3
9.3
9.600000000000001
9.6
9.900000000000002
9.9
I'm using a jQuery slider where users can select a time range between 00:00 and 1d+12:00. 36 hours all together.
Anyway.
I would like to apply min and max values to my handles based on what they're set to. These are my requirements:
left handle can never go over midnight on the next day (max is 24 hours)
left handle can never go more left than -24 hours from right handle (min is right handle value minus 24 hours)
right handle can never go more than +24 hours from the left handle (max is left handle value plus 24 hours)
As I understand, minimum and maximum values can only be applied to single handle slider control and not to range slider?
Is it possible to set minimums and maximums individually to both handles?
I've tried initializing it this way but no luck:
$(".timing-slider", timing).slider({
range: true,
min: [0, 0],
max: [24, 36],
}
This jQuery UI slider extension satisfies all upper requirements
I've managed to change default jQuery UI slider to include a few more configuration properties:
minRangeSize - sets minimum range size so ranges can't be narrower than this setting
maxRangeSize - sets maximum range size so ranges can't be wider than this setting
autoShift - when set to true it automatically drags the other handle along when range width reaches maximum; when set to false handle just can't be moved beyond maximum range width
lowMax - sets the lower handle upper boundary so it's impossible to set lower handle beyond this value
topMin - sets the upper handle lower boundary so it's impossible to set upper handle below this value
This is a working example of such range slider.
This is the extra code that has to be run after jQuery slider. It actually rewrites one of its internal functions to also check the new settings. This code will only change slider code when slider script has been loaded (hence the first if statement that checks whether slider widget has been loaded):
(function ($) {
if ($.ui.slider)
{
// add minimum range length option
$.extend($.ui.slider.prototype.options, {
minRangeSize: 0,
maxRangeSize: 100,
autoShift: false,
lowMax: 100,
topMin: 0
});
$.extend($.ui.slider.prototype, {
_slide: function (event, index, newVal) {
var otherVal,
newValues,
allowed,
factor;
if (this.options.values && this.options.values.length)
{
otherVal = this.values(index ? 0 : 1);
factor = index === 0 ? 1 : -1;
if (this.options.values.length === 2 && this.options.range === true)
{
// lower bound max
if (index === 0 && newVal > this.options.lowMax)
{
newVal = this.options.lowMax;
}
// upper bound min
if (index === 1 && newVal < this.options.topMin)
{
newVal = this.options.topMin;
}
// minimum range requirements
if ((otherVal - newVal) * factor < this.options.minRangeSize)
{
newVal = otherVal - this.options.minRangeSize * factor;
}
// maximum range requirements
if ((otherVal - newVal) * factor > this.options.maxRangeSize)
{
if (this.options.autoShift === true)
{
otherVal = newVal + this.options.maxRangeSize * factor;
}
else
{
newVal = otherVal - this.options.maxRangeSize * factor;
}
}
}
if (newVal !== this.values(index))
{
newValues = this.values();
newValues[index] = newVal;
newValues[index ? 0 : 1] = otherVal;
// A slide can be canceled by returning false from the slide callback
allowed = this._trigger("slide", event, {
handle: this.handles[index],
value: newVal,
values: newValues
});
if (allowed !== false)
{
this.values(index, newVal, true);
this.values((index + 1) % 2, otherVal, true);
}
}
} else
{
if (newVal !== this.value())
{
// A slide can be canceled by returning false from the slide callback
allowed = this._trigger("slide", event, {
handle: this.handles[index],
value: newVal
});
if (allowed !== false)
{
this.value(newVal);
}
}
}
}
});
}
})(jQuery);